通用问题...从通用类中调用静态方法

3

我有一个通用类:

public class MyList<LinkedItem> : List<LinkedItem> where LinkedItem : MyItem, new()
{
}

我希望能够从该通用类访问LinkedItem类的静态函数,该类是MyItem类的后代(因此无需创建LinkedItem的实例)。

这是否可能?

谢谢,

Eric

6个回答

2

直接从类型参数中无法实现此操作,因为您无法在泛型类型参数上调用静态方法(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);

1
你说,“不可能做到”。但是有三个答案证明它是可能的,并且说明了如何实现。你如何反驳那些认为你的回答误导了他们的人? - Jon Hanna
1
@Jon,即使面对其他答案,我的回答也是正确的。使用反射不是在泛型类型参数上调用方法,而是在泛型类型参数的类型上调用它。这两种情况之间存在实质性的区别。 - JaredPar

2

这可以通过反射实现。由于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());
}

2
是的,这是可能的,但您必须使用反射通过从typeof(T).GetMethod("Foo", BindingFlags.Public | BindingFlags.Static)获取MethodInfo,然后在其上调用Invoke

这可以非常有用,特别是如果在ConstructorInfo而不是MethodInfo上使用相同的技术,以创建使用构造函数中的参数的通用工厂。不过,这种方法应该谨慎使用。特别地,在编译时无法保证所涉及的类型具有所需签名的静态方法,因此类型安全性丢失,并且这样的错误直到运行时才能被捕获。


0

这是不可能的。无法声明一个约束条件,指定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();
    }
}

1
+1 我不知道为什么你被谁踩了,但我已经把它平衡了 :) - Abel

0

默认情况下,这是不可能的。但是,如果您知道要调用的方法的名称,并且您确定每个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:同时,当我打这个字的时候,其他人似乎也在建议同样的方法 ;-)


0

虽然可能需要使用反射,但完全可以实现你所述的方式。您需要在基类中实现一个非静态访问方法,并在每个特定的继承类中进行重写。

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();


1
静态方法无法被定义为虚拟的,在编译时会报错。 - Elisha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接