对泛型类型参数调用静态方法

本文关键字:静态方法 调用 泛型类型参数 | 更新日期: 2023-09-27 17:47:24

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

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

我得到一个编译时错误:

"T"是"类型参数",在给定上下文中无效。

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

对泛型类型参数调用静态方法

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

T.StaticMethodOnSomeBaseClassThatReturnsCollection

可以与没有什么不同

SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection

遍历泛型类型参数是不必要的间接操作,因此不受支持。

为了详细说明前面的答案,我认为这里的反思更接近您想要的。我可以给出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,但它按要求回答了问题,那么它可能会崩溃。

你可以做我称之为代理单例的事情,我一直把它当作一种"静态继承";

interface IFoo<T> where T : IFoo<T>, new()
{
    ICollection<T> ReturnsCollection();
}
static class Foo<T> where T : IFoo<T>, new()
{
    private static readonly T value = new();
    public static ICollection<T> ReturnsCollection() => value.ReturnsCollection();
}
// Use case
public ICollection<T> DoSomething<T>() where T : IFoo<T>, new()
{
    return Foo<T>.ReturnsCollection();
}

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

听起来你正试图使用泛型来解决C#中没有"虚拟静态方法"这一事实。

不幸的是,这行不通。

我只是想说,有时委托会根据上下文解决这些问题。

如果您需要将静态方法调用为某种工厂或初始化方法,那么您可以声明一个委托,并将静态方法传递给相关的泛型工厂或任何需要此"具有此静态方法的泛型类"的工厂。

例如:

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方法,将它们作为实例方法等。

您应该能够使用反射来实现这一点,正如这里所描述的

由于链接已失效,我在折返机中发现了相关细节:

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

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

如何使用relation调用此方法?

它变得非常简单…这就是调用静态泛型的方法使用反射的方法:

// 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" });

到目前为止,您还不能。你需要一种方法告诉编译器T有这个方法,而目前还没有这种方法。(许多人正在推动微软扩大通用约束中可以指定的内容,所以也许这在未来是可能的)。

这里,我发布了一个有效的例子,它是一个变通方法

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