我有一个通用类:
public class MyList<LinkedItem> : List<LinkedItem> where LinkedItem : MyItem, new()
{
}
我希望能够从该通用类访问LinkedItem类的静态函数,该类是MyItem类的后代(因此无需创建LinkedItem的实例)。
这是否可能?
谢谢,
Eric
我有一个通用类:
public class MyList<LinkedItem> : List<LinkedItem> where LinkedItem : MyItem, new()
{
}
我希望能够从该通用类访问LinkedItem类的静态函数,该类是MyItem类的后代(因此无需创建LinkedItem的实例)。
这是否可能?
谢谢,
Eric
直接从类型参数中无法实现此操作,因为您无法在泛型类型参数上调用静态方法(C# Lang Spec第4.5节)。
类型参数不能用于成员访问(§7.5.4)或类型名称(§3.8)以标识静态成员或嵌套类型。
是的,通过反射技巧可以实现这一点,正如其他人所指出的那样。但一般来说,使用反射来解决简单的方法调用场景表明设计不良。
一个更好的设计是传递一个工厂/委托,以类型安全的方式封装静态方法。
class MyItem : MyItem {
static void TheFunction() { ... }
}
public class MyList<LinkedItem> : List<LinkedItem> where LinkedItem : MyItem, new()
{
public MyList(Action theStaticFunction) {
...
}
}
new MyList<MyItem>(MyItem.TheFunction);
这可以通过反射实现。由于C#没有对静态成员施加API约束,因此没有直接的方法来执行此操作。
我不确定您所处的场景是什么,但在大多数情况下,这不是推荐的解决方案 :)
public class MyList<LinkedItem> : List<LinkedItem>
where LinkedItem : MyItem, new()
{
public int CallStaticMethod()
{
// Getting a static method named "M" from runtime type of LinkedItem
var methodInfo = typeof(LinkedItem)
.GetMethod("M", BindingFlags.Static | BindingFlags.Public);
// Invoking the static method, if the actual method will expect arguments
// they'll be passed in the array instead of empty array
return (int) methodInfo.Invoke(null, new object[0]);
}
}
public class MyItem
{
}
class MyItemImpl : MyItem
{
public MyItemImpl()
{
}
public static int M()
{
return 100;
}
}
100
:public void Test()
{
Console.WriteLine(new MyList<MyItemImpl>().CallStaticMethod());
}
typeof(T).GetMethod("Foo", BindingFlags.Public | BindingFlags.Static)
获取MethodInfo,然后在其上调用Invoke
。
这可以非常有用,特别是如果在ConstructorInfo而不是MethodInfo上使用相同的技术,以创建使用构造函数中的参数的通用工厂。不过,这种方法应该谨慎使用。特别地,在编译时无法保证所涉及的类型具有所需签名的静态方法,因此类型安全性丢失,并且这样的错误直到运行时才能被捕获。
这是不可能的。无法声明一个约束条件,指定LinkedItem
参数必须包含所需的静态方法。
可能最接近的方法是:
public class ILinkedItemFactory<T>
{
void YourMethodGoesHere();
}
public class MyList<LinkedItem, Factory> : List<LinkedItem>
where Factory : ILinkedItemFactory<LinkedItem>
where LinkedItem : MyItem, new()
{
public MyList(Factory factory)
{
factory.YourMethodGoesHere();
}
}
默认情况下,这是不可能的。但是,如果您知道要调用的方法的名称,并且您确定每个LinkedItem
类型都将包含此方法,则可以使用反射来实现您的目标。注意:对于一般编程任务,通常有更好的方法而不是解决反射问题。
以下代码将始终输出true
,并调用一个静态成员,该成员始终可用(我删除了您的泛型类型约束,因为在静态方法中这不重要)。
public class MyList<LinkedItem> : List<LinkedItem>
{
public bool DoSomething()
{
Type t = typeof(LinkedItem);
object o = new Object();
var result = t.InvokeMember("ReferenceEquals",
BindingFlags.InvokeMethod |
BindingFlags.Public |
BindingFlags.Static,
null,
null, new[] { o, o });
return (result as bool?).Value;
}
}
// call it like this:
MyList<string> ml = new MyList<string>();
bool value = ml.DoSomething(); // true
PS:同时,当我打这个字的时候,其他人似乎也在建议同样的方法 ;-)
虽然可能需要使用反射,但完全可以实现你所述的方式。您需要在基类中实现一个非静态访问方法,并在每个特定的继承类中进行重写。
public class MyItem
{
public static void DoSomeStaticStuff() { //DoSomeStaticStuff for MyItem }
public virtual void AccessSomeStaticStuff() { MyItem.DoSomeStaticStuff(); }
}
public class SomeItem : MyItem
{
public static void DoSomeStaticStuff() { //DoSomeStaticStuff for SomeItem }
public override void AccessSomeStaticStuff() { SomeItem.DoSomeStaticStuff(); }
}
然后在你的类中,有约束条件 T : MyItem,你只需要调用 T.AccessSomeStaticStuff();