![](/img/trans.png)
[英]Accessing value of a readonly static field results in object reference error using reflection c#
[英]C# - Get FieldInfo value from static readonly members of static classes using reflection
我正在尝试制作一个使用反射的系统,并且在编译期间,它能够为某些类生成配置文件。 配置文件的使用是为了避免在运行时使用反射。 让我解释。
我有类似的东西
public abstract class BaseClass
{
public abstract string ID { get; }
}
和多种实现,如
public class ClassOne : BaseClass
{
public override string ID { get { return "Class1"; } }
}
public class ClassTwo : BaseClass
{
public override string ID { get { return "Class2"; } }
}
public class ClassThree : BaseClass
{
public override string ID { get { return "Class3"; } }
}
为了能够以静态方式在适当的上下文中使用每个实现,我所做的是为每个上下文创建一个静态类,如下所示:
namespace ContextOne
{
internal static class Container
{
public static readonly ClassOne = new ClassOne();
}
}
通过这种方式,与所有上下文共享 BaseClass,我可以创建多个子项,为特定用途定义特定值。 在上下文中,我可以像这样定义一个 .cs 文件:
namespace MyContext
{
public class MyClass : BaseClass
{
public override string ID { get { return "MyClass"; } }
}
public class YourClass : BaseClass
{
public override string ID { get { return "YourClass"; } }
}
internal static class Container
{
public static readonly MyClass Option1 = new MyClass();
public static readonly YourClass Option2 = new YourClass();
}
}
我还创建了一个静态类,其中静态构造函数使用反射来获取在项目的所有程序集中定义的所有成员,以便它可以保持所有这些类的更新内部缓存与它们相关联的配置保存在文本文件。 缓存是一个字典,其中我使用每个类中定义的 ID 作为键,使用一个 ConfigClass 作为值。 我禁用了所有在运行时使用反射的代码的执行。 这是这个类的一个非常简化的版本:
public static class Manager
{
private static readonly Dictionary<string, ConfigClass> _cache = new Dictionary<string, ConfigClass>();
static Manager()
{
_cache = LoadFile();
// get a collection of valid IDs defined in the project.
List<string> validIDs = new List<string>();
List<Type> containers = GetTypesWithNameInAllAssemblies(".Container", true);
foreach(var cont in containers)
{
MemberInfo[] members = cont.GetMembers(BindingFlag.Static | BindingFlag.Public);
foreach(var mb in members)
{
FieldInfo field = cont.GetField(mb.Name);
if(field.FieldType.BaseType == typeof(BaseType)
{
//*********************
string id = /* TODO */;
//*********************
validIDs.Add(id);
}
}
}
// update the cache.
AddMissingEntriesToCache(validIDs, _cache);
RemoveObsoleteEntriesFromCache(validIDs, _cache);
SaveFile(_cache);
}
private static List<Type> GetTypesWithNameInAllAssemblies(string name, bool fullNameEndsWith = false)
{
List<Type> wantedTypes = new List<Type>();
Assembly[] assemblies = GetAssemblies();
for (int i = 0; i < assemblies.Length; i++)
{
Type[] types = assemblies[i].GetTypes();
for (int j = 0; j < types.Length; j++)
{
if (fullNameEndsWith)
{
if (types[j].FullName.EndsWith(name))
{
wantedTypes.Add(types[j]);
}
}
else
{
if (types[j].Name == name)
{
wantedTypes.Add(types[j]);
}
}
}
}
return wantedTypes;
}
}
正如您从代码中看到的,我无法从通过反射获得的 FieldInfo 对象中获取 ID。 我尝试使用
string id = field.GetValue(null) as string;
但我显然犯了一个错误:我可能需要获取 Container 中定义的派生类的静态实例才能做到这一点,但我不知道如何去做。 可能,一旦定义了实例,它应该坚持静态成员,这样我就不会创建导致内存泄漏或其他类型问题的其他实例。
非常感谢 !! :)
您已经完成了所有艰苦的工作,这一切归结为需要从静态容器中获取类的实例,然后读取 ID:
if(field.FieldType.BaseType == typeof(BaseClass))
{
var instance = (BaseClass)field.GetValue(null); // null as this is a static field
//*********************
string id = instance.ID;
//*********************
validIDs.Add(id);
}
我已经制作了一个简化的代码示例来向您展示: https : //dotnetfiddle.net/rEjJHw
在这种情况下, field
实际上是类的实例,而不是 ID 属性,这就是您的代码不起作用的原因。
如果您只创建该ID
属性来重复类名,您可以只完成field.FieldType.Name
而根本不需要实际获取实例。
据我了解,类的 ID 是固定的,类 ClassOne 的所有实例共享相同的 ID - 所以实际上 ID 不应该是类的属性,而是一些元信息。
如果是这样,您应该创建 IDAttribute 并用它标记您的类。 您仍然可以在 BaseClass 中拥有 ID 属性,它将从属性中获取 ID。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.