C#中反射获取基类静态成员

今天在实现单例的时候,通过继承泛型基类,子类型就可以拥有静态 Instance 属性成员,但是当我通过子类类型获取 Instance 对象时,却没有办法获取到,原因是因为反射时,虽然会在继承的基类中进行查找,但是只限于实例成员,对于静态成员是不进行继承查找的。

下面看一下具体的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System;
using System.Reflection;

class Program
{
static void Main(string[] args)
{
SubA inst = Create<SubA>();
Console.WriteLine(inst); // null

Console.ReadLine();
}

public static T Create<T>()
where T:class
{
Type closeType = typeof(Singleton<>).MakeGenericType(typeof(T));
if(closeType.IsAssignableFrom(typeof(T)))
{
// 不能获取 PropertyInfo,因为静态成员不会进行查找
return typeof(T).GetProperty("Instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) as T;
}
else
{
return Activator.CreateInstance<T>();
}
}
}

public class Singleton<T>
where T : class, new()
{
private class SingletonWrapper
{
public static T instance;

static SingletonWrapper()
{
instance = new T();
}
}

public static T Instance
{
get
{
return SingletonWrapper.instance;
}
}
}

public class SubA:Singleton<SubA>{}

上面代码没有办法正确的获取到 Instance 对象,因为 C# 不会在基类中查找静态成员,因此只能在 Singleton<T> 中进行查找,修改获取方式:

1
2
3
4
5
6
Type closeType = typeof(Singleton<>).MakeGenericType(typeof(T));
if(closeType.IsAssignableFrom(typeof(T)))
{
//return typeof(T).GetProperty("Instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) as T;
return closeType.GetProperty("Instance")?.GetValue(null) as T;
}

另外这种方式实现单例,存在一个问题,特别需要注意,还是接着上面的代码:

1
2
3
4
5
6
7
8
9
10
11
public class GSubA:SubA{}
public class GSubB:SubA{}

public class SubB : Singleton<SubB>{}
public class SubC : Singleton<SubC>{}

// true
bool isSame = GSubA.Instance == GSubB.Instance;

// false
isSame = SubB.Instance == SubC.Instance;

首先解释第二条为什么为 false,虽然看上去都是继承自 Singleton<T> 基类,但是其他是继承自不同的类型,Singleton<T> 叫做开放类型,当给泛型参数指定具体类型时,就变为闭合类型,也就是说 C# 会在运行时,创建两个新的类型 Singleton<SubB>Singleton<SubC>,不同的类型,其静态成员肯定是不同的。

在理解了上面的解释之后,在看第一条就容易理解了,因为 GSubAGSubB都继承自同一类型 SubA,所以对应的 Instance 成员相同。

总结一下就两点:

  • C# 反射静态成员时,不会在继承结构中查找,只能通过使用静态声明类型来获取;
  • 泛型类会根据泛型参数不同,生成不同的闭合类型。