C# 渴望静态构造函数
我已经阅读了一些关于堆栈溢出的问题和答案,以及一些博客文章(包括Jon Skeet的lazy singleton initialization),它们似乎都专注于使初始化尽可能地懒惰。静态初始化基本上有两种选择:C# 渴望静态构造函数,c#,initialization,C#,Initialization,我已经阅读了一些关于堆栈溢出的问题和答案,以及一些博客文章(包括Jon Skeet的lazy singleton initialization),它们似乎都专注于使初始化尽可能地懒惰。静态初始化基本上有两种选择: 第一次引用类的实例或静态成员时 在程序开始和第一次引用之间的未指定时间 有没有办法让静态构造函数(或某种形式的初始化代码)在程序开始时为特定类运行 上下文:我们的库将解析传入的XML和返回对象。返回的对象类型取决于正在解析的XML元素。我们提供了两个简单的类:一个是非常基本的类,允
- 第一次引用类的实例或静态成员时
- 在程序开始和第一次引用之间的未指定时间
听起来您需要某种“模块或程序集初始值设定项”。我不认为这样的东西存在于IL中(尽管我可能错了),而且在C#中肯定不存在 您总是可以创建某种类型的属性,然后使用反射查找用该属性修饰的所有类型,并显式初始化它们。(请注意,泛型类型变得更加棘手……您可能希望将其限制为非泛型类型。) 编辑:我找到了更多的选项:
- 创建一个初始值设定项,该初始值设定项查找
ModuleInitializer。使用Mono-Cecil在任何命名空间中运行
- 给
public static void RegisterAllParsers(Assembly assembly)
。。。这可能是基于属性的。当然,它只能明智地选择静态解析方法——如果任何开发人员有一个工厂,它可以根据工厂的初始化以不同的方式进行解析,那么您就无法轻松地自动注册它
然后,开发人员需要调用:
LibraryClass.RegisterAllParsers(typeof(SomeTypeInProgram).Assembly);
启动时。要记住这一点可能不太难——而且大多数应用程序只有一个入口点,或者至少有一些通用的启动代码。Afaik没有明确的方法,但是您可以创建如下内容(我现在警告您,这很难看,也不快): 然后,当您的应用程序加载时,使用类似这样的方法使用自定义属性初始化所有内容:
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
var a = Attribute.GetCustomAttribute(type, typeof(AppInitialized), true)
as AppInitialized;
if (a != null)
a.Initialize();
}
有点像@Flyingstrudel,我也拼凑了一些“有点”符合你的要求的东西: 属性:
[AttributeUsage(AttributeTargets.All)]
public class ModuleInitializerAttribute : Attribute
{
private readonly string _assemblyName;
private readonly Func<Module, bool> _modulePredicate;
private readonly string _typeName;
private readonly string _methodName;
/// <summary>
/// Only used in my test rig so I can make sure this assembly is loaded
/// </summary>
public static void CallMe() {}
public ModuleInitializerAttribute(string assemblyName, string moduleName, string typeWithMethod, string methodToInvoke)
{
_assemblyName = assemblyName;
_modulePredicate = mod => moduleName == null || mod.Name.Equals(moduleName, StringComparison.OrdinalIgnoreCase);
_typeName = typeWithMethod;
_methodName = methodToInvoke;
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
AppDomain.CurrentDomain.DomainUnload += AppDomainUnloading;
CheckLoadedAssemblies();
}
private void CheckLoadedAssemblies()
{
AppDomain.CurrentDomain.GetAssemblies().ToList().ForEach(this.CheckAssembly);
}
private void AppDomainUnloading(object sender, EventArgs e)
{
// Unwire ourselves
AppDomain.CurrentDomain.AssemblyLoad -= this.OnAssemblyLoad;
AppDomain.CurrentDomain.DomainUnload -= AppDomainUnloading;
}
private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
CheckAssembly(args.LoadedAssembly);
}
private void CheckAssembly(Assembly asm)
{
if (asm.FullName == _assemblyName)
{
var module = asm.GetModules().FirstOrDefault(_modulePredicate);
if (module != null)
{
var type = module.GetType(string.Concat(asm.GetName().Name, ".", _typeName));
if (type != null)
{
var method = type.GetMethod(_methodName);
if (method != null)
{
method.Invoke(null, null);
}
}
}
}
}
}
另一个类库:
namespace ClassLibrary1
{
public class SomeOtherClass
{
}
public static class ModuleInitializerTest
{
public static void ModuleInitialize()
{
// Do interesting stuff here?
}
}
}
我强烈建议为此(
System.ComponentModel.Composition
namespace)考虑使用。然后,您的客户端只需添加一个[Export(typeof(isomparserinterface))]
属性,MEF就可以为您的解析器提供所有可用的扩展
您甚至可以使用来允许您的代码只实例化它遇到的元素实际需要的解析器
[Export(typeof(ISomeParserInterface))]
[ExportMetadata("ElementName", "SomeXmlElement")]
您可以根据XML解析的当前上下文来确定对特定XML元素使用哪个解析器。 将从XML解析的每个CLR对象都将作为其成员(字段或属性)包含在其他CLR对象中,根对象除外。所以XML解析器可以由成员类型(字段类型或属性类型)确定。 对于必须解析XML的根CLR对象,必须显式指定类型 下面是较长的C#示例代码: XmlParserLibrary项目:
using System;
using System.Collections.Generic;
using System.Xml;
namespace XmlParserLibrary
{
public sealed class XmlParser
{
private readonly IDictionary<Type, IXmlParser> parsers = new Dictionary<Type, IXmlParser>()
{
{ typeof(string), new StringXmlParser() }
};
public T Parse<T>(XmlReader reader)
{
return (T)this.Parse(reader, typeof(T));
}
public object Parse(XmlReader reader, Type type)
{
// Position on element.
while (reader.Read() && reader.NodeType != XmlNodeType.Element) ;
return GetParser(type).Parse(reader);
}
private IXmlParser GetParser(Type type)
{
IXmlParser xmlParser;
if (!this.parsers.TryGetValue(type, out xmlParser))
this.parsers.Add(type, xmlParser = this.CreateParser(type));
return xmlParser;
}
private IXmlParser CreateParser(Type type)
{
var xmlParserAttribute = Attribute.GetCustomAttribute(type, typeof(XmlParserAttribute)) as XmlParserAttribute;
return xmlParserAttribute != null ? Activator.CreateInstance(xmlParserAttribute.XmlParserType) as IXmlParser : new FallbackXmlParser(this, type);
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public sealed class XmlParserAttribute : Attribute
{
public Type XmlParserType { get; private set; }
public XmlParserAttribute(Type xmlParserType)
{
this.XmlParserType = xmlParserType;
}
}
public interface IXmlParser
{
object Parse(XmlReader reader);
}
internal sealed class StringXmlParser : IXmlParser
{
public object Parse(XmlReader reader)
{
return reader.ReadElementContentAsString();
}
}
internal sealed class FallbackXmlParser : IXmlParser
{
private readonly XmlParser xmlParser;
private readonly Type type;
public FallbackXmlParser(XmlParser xmlParser, Type type)
{
this.xmlParser = xmlParser;
this.type = type;
}
public object Parse(XmlReader reader)
{
var item = Activator.CreateInstance(this.type);
while (reader.Read())
switch (reader.NodeType)
{
case XmlNodeType.Element:
var propertyInfo = this.type.GetProperty(reader.LocalName);
var propertyValue = this.xmlParser.Parse(reader.ReadSubtree(), propertyInfo.PropertyType);
propertyInfo.SetValue(item, propertyValue, null);
break;
}
return item;
}
}
}
using System.Xml;
using XmlParserLibrary;
namespace XmlParserLibraryTest
{
class Program
{
static void Main(string[] args)
{
var xmlParser = new XmlParser();
Letter letter;
using (var reader = XmlReader.Create("Letter.xml"))
letter = xmlParser.Parse<Letter>(reader);
}
}
public class Letter
{
public LetterAssociate Sender { get; set; }
public LetterAssociate Receiver { get; set; }
public LetterContent Content { get; set; }
}
public class LetterAssociate
{
public string Name { get; set; }
public string Address { get; set; }
}
[XmlParser(typeof(LetterContentXmlParser))]
public class LetterContent
{
public string Header { get; set; }
public string Body { get; set; }
}
internal class LetterContentXmlParser : IXmlParser
{
public object Parse(XmlReader reader)
{
var content = new LetterContent();
while (reader.Read())
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.LocalName)
{
case "Header":
content.Header = reader.ReadElementContentAsString();
break;
case "Body":
content.Body = reader.ReadElementContentAsString();
break;
}
break;
}
return content;
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<Letter>
<Sender>
<Name>Sender name</Name>
<Address>Sender address</Address>
</Sender>
<Receiver>
<Name>Receiver name</Name>
<Address>Receiver address</Address>
</Receiver>
<Content>
<Header>This is letter header.</Header>
<Body>This is letter body.</Body>
</Content>
</Letter>
使用系统;
使用System.Collections.Generic;
使用System.Xml;
命名空间XmlParserLibrary
{
公共密封类XmlParser
{
专用只读IDictionary解析器=新字典()
{
{typeof(string),新的StringXmlParser()}
};
公共T解析(XmlReader)
{
返回(T)this.Parse(reader,typeof(T));
}
公共对象解析(XmlReader,类型)
{
//元素上的位置。
while(reader.Read()&&reader.NodeType!=XmlN
using System.Xml;
using XmlParserLibrary;
namespace XmlParserLibraryTest
{
class Program
{
static void Main(string[] args)
{
var xmlParser = new XmlParser();
Letter letter;
using (var reader = XmlReader.Create("Letter.xml"))
letter = xmlParser.Parse<Letter>(reader);
}
}
public class Letter
{
public LetterAssociate Sender { get; set; }
public LetterAssociate Receiver { get; set; }
public LetterContent Content { get; set; }
}
public class LetterAssociate
{
public string Name { get; set; }
public string Address { get; set; }
}
[XmlParser(typeof(LetterContentXmlParser))]
public class LetterContent
{
public string Header { get; set; }
public string Body { get; set; }
}
internal class LetterContentXmlParser : IXmlParser
{
public object Parse(XmlReader reader)
{
var content = new LetterContent();
while (reader.Read())
switch (reader.NodeType)
{
case XmlNodeType.Element:
switch (reader.LocalName)
{
case "Header":
content.Header = reader.ReadElementContentAsString();
break;
case "Body":
content.Body = reader.ReadElementContentAsString();
break;
}
break;
}
return content;
}
}
}
<?xml version="1.0" encoding="utf-8" ?>
<Letter>
<Sender>
<Name>Sender name</Name>
<Address>Sender address</Address>
</Sender>
<Receiver>
<Name>Receiver name</Name>
<Address>Receiver address</Address>
</Receiver>
<Content>
<Header>This is letter header.</Header>
<Body>This is letter body.</Body>
</Content>
</Letter>