使用受限制的泛型类型C#的静态方法

7

我有一个通用类:

public class Foo<T> where T: Interface
{

}

T被强制实现的接口内有2个静态方法。

在构造函数中,我希望能够基本上执行以下操作:

public Foo()
{
   value1 = T.staticmethod1();
   value2 = T.staticmethod2();
}

使用我在上面发布的伪代码无法完成此操作。不可能以这种方式调用这些静态方法吗?


11
我感到困惑,静态方法不能存在于接口中。 - tbridge
1
接口中不能有静态方法:http://discuss.joelonsoftware.com/default.asp?dotnet.12.305680.12 - pstrjds
1
接口不能声明它们的方法是静态的,也不能在接口中放置任何具体代码。您是指抽象类而不是接口吗? - Dismissile
哎呀,你不能在接口上有一个静态方法。 - Justin Beckwith
接口中可以有静态方法,只是 C# 不支持。 - Jordão
显示剩余2条评论
5个回答

9
你可以使用扩展方法。这种技术被称为伪混合。 虽然扩展方法实际上是静态的,但它们“假装”是实例方法,所以你仍然需要一个具体的T实例。
此外,如果你希望你的接口保持其作为自我记录的“合同”的角色,指定你的T类应该具有哪些方法,这有点欺骗性。 但是它们是类型安全的(如果你不将IBarExtensions引入范围,你的Foo类将无法编译)。
//our interface
public interface IBar {}

// the two static methods are define as extension methods
public static class IBarExtensions {
    public static string someMethod1(this IBar self) {
        return "my initialization 1";
    }
    public static string someMethod2(this IBar self) {
        return "my initialization 2";
    }
}

public class Foo<T> where T : IBar, new()
{
    public string value1 {get; private set;}
    public string value2 {get; private set;}

    public Foo() {
        T t = new T(); // we can do this because of the "new()" constraint
                           // in the class definition
                           // Alternatively we could pass an instance of T in
                           // the constructor 
                           // public Foo(T t)
        value1 = t.someMethod1();
        value2 = t.someMethod2();       
    }
}

测试

public class TestBar : IBar {}
void Main()
{
    var c = new TestBar();

    var t = new Foo<TestBar>();
    Console.WriteLine(t.value1);
}

好的,我知道,重新启动一个旧的线程:更多邪恶的技巧放入我的口袋,嘿嘿嘿嘿嘿! - shelleybutterfly

4
不,这是不可能的。即使使用 dynamic 也不行。虽然有通用约束(如接口),但仅适用于实例成员。你可以考虑将这些方法作为 Func<T>/Action<T> 委托传递(参数)吗?
除此之外,您唯一的选择(且不理想)是反射。或者更好的方式是:重新思考我们的方法。

1
可怜的 pquest 将不得不回答你的问题 :) - maxbeaudoin
顺便问一下,如果给定一个不满足new()限制的泛型类型T,是否有办法强制运行静态类构造函数(如果它还没有运行)?是否有任何方法可以强制一个类运行其父类的静态构造函数(如果它还没有运行),而无需硬编码父类的名称? - supercat
@supercat 不可靠,尤其是在clr 4中的beforefieldinit更改方面。 - Marc Gravell
@Marc Gravell:太遗憾了;否则就可以编写一个DoSomething<T>方法,它可以在不显式使用反射的情况下调用T的最派生超类型的DoSomething方法,该方法“注册”了一个(使用静态注册列表)。 但要使其工作,静态类必须在需要它们之前注册其方法。 - supercat

1

C# 不允许调用接口中定义的静态方法。它会发出一个命名不当的错误信息:

如果你使用一个允许在接口中使用静态成员的语言编写的库,并尝试从 C# 访问静态成员,则也会出现 CS0017。

如果 C# 允许,你可以使用接口名称而不是泛型参数名称来调用它们:

// doesn't work:
public Foo() {
  value1 = Interface.staticmethod1();
  value2 = Interface.staticmethod2(); 
} 

所以,你有几个选择:

  1. 使用允许调用这些成员的语言(我认为VB.NET和C++/CLI允许这样做)。你可以编写一个小的适配器,从C#中使用它;
  2. 询问提供此接口的人(也许是你自己)不要在接口中使用静态成员。它们可以移动到单独的静态类中。该类甚至可以嵌套在接口中(如果语言允许),并且可以从C#中工作。

C# 不允许在接口中定义静态方法。 - Dismissile
没错,甚至不用称呼它们... 但是它们可以在其他语言中定义。 - Jordão

1

由于特定类型中静态成员的存在并不能说明派生类型是否具有与该名称兼容的成员,因此无法使用受限泛型类型参数的静态成员。假设类型“Foo”具有返回Int32的静态函数Wowzo(),类型DerivedFoo1具有不同的返回Int32的静态函数Wowzo(),类型DerivedFoo2(从Foo派生)具有返回String的静态成员Wowzo(),而类型DerivedFoo3具有名为Wowzo的嵌套类。如果T是被限制为Foo后代的类型参数,那么T.Wowzo是什么?


0

你也可以将一个非静态方法作为包装器来调用你的静态方法。这个非静态方法可以成为你接口的一部分。


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