【问题标题】:Calling a static method on a generic type parameter在泛型类型参数上调用静态方法
【发布时间】:2010-09-16 20:02:59
【问题描述】:

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

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

我得到一个编译时错误:

“T”是一个“类型参数”,在给定的上下文中是无效的。

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

【问题讨论】:

标签: c# generics


【解决方案1】:

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

【讨论】:

  • 问题在于,因为泛型是由运行时提供的,这可能意味着一个新的 CLR 版本——自 2.0 以来他们(很大程度上)避免了这个版本。不过,也许我们该换一个新的了……
【解决方案2】:

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

很遗憾,这行不通。

【讨论】:

  • 我不是——我在生成的 DAL 层之上工作。生成的类都继承自具有静态 FetchAll 方法的基类。我正在尝试使用“通用”存储库类来减少存储库类中的代码重复 - 大量重复代码,除了使用的具体类。
  • 那你为什么不直接调用 SomeBaseClass.StaticMethod...() 呢?
  • 对不起,我在之前的评论中没有很好地解释自己。 FetchAll 在基础上定义,但在派生类上实现。我需要在派生类上调用它。
  • 如果是静态方法,那么它既是由base定义又是实现。 C# 中没有虚拟/抽象静态方法之类的东西,也没有覆盖。我怀疑你只是重新声明了它,这是非常不同的。
  • 是的,你是对的——我在这里做了无效的假设。感谢您的讨论,它帮助我走上了正轨。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

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

    T.StaticMethodOnSomeBaseClassThatReturnsCollection
    

    ...可以与:

    SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection
    

    遍历泛型类型参数是一种不需要的间接方式,因此不受支持。

    【讨论】:

    • 但是如果你在子类中屏蔽了你的静态方法呢? public class SomeChildClass : SomeBaseClass{ public new static StaticMethodOnSomeBaseClassThatReturnsCollection(){} } 你能做些什么来从泛型类型访问那个静态方法吗?
    • 在下面查看 Joshua Pech 的答案,我相信在这种情况下会起作用。
    • return SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection(); 会起作用吗?如果是这样,您可能希望将其添加到您的答案中。谢谢。它对我有用。就我而言,我的班级有一个类型参数,所以我做了return SomeBaseClass&lt;T&gt;.StaticMethodOnSomeBaseClassThatReturnsCollection(); 并且有效。
    【解决方案5】:

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

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

    【讨论】:

    • 这给我一个语法错误? public T : SomeBaseClass 是什么意思?
    • 如果你的类有一个实例方法 someInstanceMethod() 你总是可以通过 (new T()).someInstanceMethod(); - 但这是调用实例方法 - 问题询问如何调用类的静态方法。
    【解决方案6】:

    您应该能够使用反射来做到这一点,正如 here

    所述

    由于链接失效,我在返程机中找到了相关细节:

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

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

    如何使用 reelection 调用此方法?

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

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

    【讨论】:

      【解决方案7】:

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

      【讨论】:

      • 不,一点也不。 JaredPar 完全正确: T.StaticMethodOnSomeBaseClassThatReturnsCollection 其中 T : SomeBaseClass 与简单地陈述 SomeBaseClass.StaticMethodOnSomeBaseClassThatReturnsCollection 没有什么不同。
      • 这正是我所需要的,它适用于静态方法
      • 这是我需要的答案,因为我无法控制类和基类。
      【解决方案8】:

      我只是想把它扔在那里,有时代表会根据上下文解决这些问题。

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

      例如:

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

      【讨论】:

      • 只是稍微更新一下这个答案,因为我认为这似乎是最好的模式。使用 C# 8,您应该能够通过接口强制执行静态方法。
      • @TonyTopper 我真的希望使用 C# 8s 静态方法,我们能够使用像 where T : ISomeInterfaceWithStaticMethods 这样的约束来调用 T.SomeStaticMethod(),但可惜它仍然给出“'T' is a type参数,在给定的上下文中无效”。我很确定编译器有足够的信息在编译时解析具体类型,所以奇怪的是它不起作用。
      【解决方案9】:

      你可以做我所说的代理单例,我一直把它用作一种“静态继承”

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

      【讨论】:

      • 这个答案非常好,解决了我遇到的一个相当棘手的问题!
      • 这个答案太棒了。它基本上创建了一个 Ts 的隐式对象缓存,用于获取实际方法。
      猜你喜欢
      • 2015-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-14
      • 1970-01-01
      • 2011-01-07
      • 1970-01-01
      • 2017-03-03
      相关资源
      最近更新 更多