在泛型类型参数上调用静态方法


107

我希望这样做,但是在C#中似乎是非法的:


public Collection MethodThatFetchesSomething<T>()
    where T : SomeBaseClass
{
    return T.StaticMethodOnSomeBaseClassThatReturnsCollection();
}

我收到一个编译时错误:“'T'是一个'类型参数',在给定的上下文中无效。”

给定通用类型参数,如何在通用类上调用静态方法?给定约束,静态方法必须可用。


Answers:


58

在这种情况下,您应该直接在约束类型上调用static方法。C#(和CLR)不支持虚拟静态方法。所以:

T.StaticMethodOnSomeBaseClassThatReturnsCollection

...没有什么不同:

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

通过泛型类型参数是不需要的间接寻址,因此不受支持。


24
但是,如果您在子类中屏蔽了静态方法,该怎么办?public class SomeChildClass:SomeBaseClass {public new static StaticMethodOnSomeBaseClassThatReturnsCollection(){}}您能做些什么来从泛型类型访问该静态方法吗?
雨果·米格纳隆

2
在下面查看Joshua Pech的答案,我相信在这种情况下会有用。
Remi Despres-Smyth,

1
return SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection();工作吗?如果是这样,您可能希望将其添加到您的答案中。谢谢。它为我工作。就我而言,我的班级有一个类型参数,所以我做到了return SomeBaseClass<T>.StaticMethodOnSomeBaseClassThatReturnsCollection();,而且行得通。
toddmo

27

为了详细说明先前的答案,我认为反思与您在此想要的更加接近。我可以给出1001个为什么或不应该做某事的原因,我将按要求回答您的问题。我认为您应该在通用参数的类型上调用GetMethod方法,然后从那里开始。例如,对于一个函数:

public void doSomething<T>() where T : someParent
{
    List<T> items=(List<T>)typeof(T).GetMethod("fetchAll").Invoke(null,new object[]{});
    //do something with items
}

其中T是具有静态方法fetchAll()的任何类。

是的,我知道这太慢了,并且如果someParent不强制其所有子类都实现fetchAll却可能会崩溃,但它会按要求回答问题。


2
一点都不。JaredPar完全正确:T.StaticMethodOnSomeBaseClassThatReturnsCollection,其中T:SomeBaseClass与简单地声明SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection没有什么不同。
Remi Despres-Smyth

2
这就是我所需要的,它可以使用静态方法工作
myro 18'Apr

这是我需要的答案,因为我无法控制类和基类。
蒂姆

8

调用这种方法的唯一方法是通过反射,但是,听起来可能可以将该功能包装在接口中并使用基于实例的IoC / factory / etc模式。


5

听起来您正在尝试使用泛型来解决C#中没有“虚拟静态方法”的事实。

不幸的是,那是行不通的。


1
我不是-我正在生成的DAL层之上。生成的类均继承自具有静态FetchAll方法的基类。我正在尝试使用“通用”存储库类减少存储库类中的代码重复-除了所用的具体类以外,很多重复代码。
Remi Despres-Smyth

1
那为什么不直接调用SomeBaseClass.StaticMethod ...()?
布拉德·威尔逊,

抱歉,我在上一条评论中没有对自己做很好的解释。FetchAll在基础上定义,但在派生类上实现。我需要在派生类上调用它。
Remi Despres-Smyth

7
如果它是静态方法,那么它既是由基础定义和实现的。C#中没有诸如虚拟/抽象静态方法之类的东西,也没有对此的替代。我怀疑您只是简单地重新声明了,这是非常不同的。
马克·格雷韦尔

1
是的,您是对的-我在这里做了无效的假设。感谢您的讨论,这有助于我走上正确的道路。
Remi Despres-Smyth

2

到目前为止,您还不能。您需要一种方法告诉编译器T具有该方法,而目前尚无办法。(许多人正在推动Microsoft扩展通用约束中可以指定的内容,因此将来可能会实现这一点)。


1
问题在于,因为运行时提供了泛型,所以这可能意味着将使用新的CLR版本-从2.0开始(很大程度上)已经避免了该版本。不过,也许我们要来一个新的……
Marc Gravell

2

在这里,我发布了一个可行的示例,这是一种解决方法

public interface eInterface {
    void MethodOnSomeBaseClassThatReturnsCollection();
}

public T:SomeBaseClass, eInterface {

   public void MethodOnSomeBaseClassThatReturnsCollection() 
   { StaticMethodOnSomeBaseClassThatReturnsCollection() }

}

public Collection MethodThatFetchesSomething<T>() where T : SomeBaseClass, eInterface
{ 
   return ((eInterface)(new T()).StaticMethodOnSomeBaseClassThatReturnsCollection();
}

2
这给我语法错误吗?什么public T : SomeBaseClass意思
埃里克

如果您的类具有实例方法someInstanceMethod(),则可以通过执行(new T())。someInstanceMethod()来始终调用该实例方法。-但这正在调用实例方法-这个问题问如何调用类的静态方法。
蒂莫西

2

我只是想把它扔出去,有时代表会根据情况解决这些问题。

如果您需要将静态方法作为某种工厂或初始化方法来调用,则可以声明一个委托并将该静态方法传递给相关的泛型工厂,或者将其传递给需要该“带有该静态方法的泛型类”的对象。

例如:

class Factory<TProduct> where TProduct : new()
{
    public delegate void ProductInitializationMethod(TProduct newProduct);


    private ProductInitializationMethod m_ProductInitializationMethod;


    public Factory(ProductInitializationMethod p_ProductInitializationMethod)
    {
        m_ProductInitializationMethod = p_ProductInitializationMethod;
    }

    public TProduct CreateProduct()
    {
        var prod = new TProduct();
        m_ProductInitializationMethod(prod);
        return prod;
    }
}

class ProductA
{
    public static void InitializeProduct(ProductA newProduct)
    {
        // .. Do something with a new ProductA
    }
}

class ProductB
{
    public static void InitializeProduct(ProductB newProduct)
    {
        // .. Do something with a new ProductA
    }
}

class GenericAndDelegateTest
{
    public static void Main()
    {
        var factoryA = new Factory<ProductA>(ProductA.InitializeProduct);
        var factoryB = new Factory<ProductB>(ProductB.InitializeProduct);

        ProductA prodA = factoryA.CreateProduct();
        ProductB prodB = factoryB.CreateProduct();
    }
}

不幸的是,您不能强制该类具有正确的方法,但是您至少可以在编译时强制所产生的工厂方法具有其期望的所有内容(即,具有正确签名的初始化方法)。这比运行时反射异常更好。

这种方法也有一些好处,例如,您可以重用init方法,让它们成为实例方法,等等。


1

你应该能够做到这一点使用反射,如描述在这里

由于链接失效,我在回溯机器中找到了相关详细信息:

假设您有一个带有静态泛型方法的类:

class ClassWithGenericStaticMethod
{
    public static void PrintName<T>(string prefix) where T : class
    {
        Console.WriteLine(prefix + " " + typeof(T).FullName);
    }
}

如何使用反射调用此方法?

事实证明,这很容易...这是您使用反射调用静态泛型方法的方式:

// Grabbing the type that has the static generic method
Type typeofClassWithGenericStaticMethod = typeof(ClassWithGenericStaticMethod);

// Grabbing the specific static method
MethodInfo methodInfo = typeofClassWithGenericStaticMethod.GetMethod("PrintName", System.Reflection.BindingFlags.Static | BindingFlags.Public);

// Binding the method info to generic arguments
Type[] genericArguments = new Type[] { typeof(Program) };
MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);

// Simply invoking the method and passing parameters
// The null parameter is the object to call the method from. Since the method is
// static, pass null.
object returnValue = genericMethodInfo.Invoke(null, new object[] { "hello" });

链接已死。
Necronomicron
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.