如何将 `System.Type` 作为带有约束的泛型参数?

3

如何将 System.Type 作为有约束的泛型参数?

这里接口 I 拥有单一方法 M,类 CD 实现了该接口。

public interface I {
    void M;
}

public class C: I {
    public void M();
}
public class D: I {
    public void M();
}

Service实例化一个新的T()(实际上是C/D实现I接口),并在上面运行I接口方法。

public class Service {
    public void Fn<T>(T t): where T : Type, I, new() {
        ( new t() ).M();
        // ...
    }
}

这将最终成为服务的公共API,因此我希望它尽可能简明扼要:
public class Main {
    public void main() {
        Service.Fn( typeof( C ));
        Service.Fn( typeof( D ));
        // ...
    }
}

这段代码无法编译且出现错误:

'System.Type'必须是一个非抽象类型,具有公共的无参构造函数,才能在泛型类型或方法中用作参数'T'

最终目标是能够像Main描述的那样简洁地调用Service.Fn。 如果我的接口尝试有其他替代方案,也可以提供。


2
你是否试图创建一个 C/D?如果是的话,你应该使用一个通用参数,例如 Service.Fn<C>() - Guvante
C和D是类型,但它们不继承自“Type”。此外,您似乎不理解类型对象(例如typeof(C))和类型标记(例如Service.Fn<C>())之间的区别。 - phoog
除了语法之外,类型对象和类型标记之间有什么区别? - xst
3个回答

3
以下代码对我有效:
```html

以下代码适用于我:

```
public class Service {
    public void Fn<T>() where T : I, new() {
        (new T()).M();
        // ...
    }
}
public interface I
{
    void M();
}

public class C : I
{
    public void M();
}
public class D : I
{
    public void M();
}

static void Main()
{
    var service = new Service();
    service.Fn<C>();
    service.Fn<D>();
}

问题在于where子句中多余的Type。由于T是一个泛型参数,因此它本身就是一个类型 - 但是AB类型的对象无法转换为Type类 - 这就是where T:Type的意思。
通常,where T:X,Y,Z表示任何T必须(取决于XYZ的内容)实现接口或者是XYZ的子类。 new()约束条件略有不同,并使编译器知道您还希望能够在方法中创建T类型的新对象。 (有关更多详细信息,请参见http://msdn.microsoft.com/en-gb/library/bb384067.aspxhttp://msdn.microsoft.com/en-gb/library/d5x73970.aspx
此外,我已从输入中删除了typeOf(),因为当您使用泛型时,可以直接在函数中引用类型(请参见上文)。希望这有意义。如果您有任何问题,请随时问!

2

这个示例可能会帮助你理解这里发生的情况:

void PrintTypeName(Type t) { Console.WriteLine(t.Name); }
void PrintTypeName<T>() { Console.WriteLine(typeof(T).Name); }

void Main()
{
    Type cType = typeof(C);
    Type dType = typeof(D);
    C cInstance = new C();
    D dInstance = new D();

    PrintNameOfType(cType.GetType());     //prints "RuntimeType"
    PrintNameOfType(dType.GetType());     //prints "RuntimeType"
    PrintNameOfType(cInstance.GetType()); //prints "C"
    PrintNameOfType(dInstance.GetType()); //prints "D"

    PrintNameOfType(cType);     //prints "C"
    PrintNameOfType(dType);     //prints "D"

    //does not compile:
    //PrintNameOfType(cInstance);
    //PrintNameOfType(dInstance);

    PrintNameOfType<Type>(); //prints "Type"
    PrintNameOfType<C>();    //prints "C"
    PrintNameOfType<D>();    //prints "D"
}

棒极了的示例,Phoog! 如果可以的话,我想补充一个有用的概念,帮助我理解它们之间的区别: T 可以被认为是替换任何其他类型,例如 objectstring 等,而 typeof(T) 则是一种将类型作为变量传递的方式。 - David E
1
@DavidEdey 感谢您的评论。我更喜欢说“将类型作为对象传递”,但也许这会让人感到困惑。当然,真正的重点是Type是描述类型的类,而Customer是描述客户的类。人们会因为Customer是一种类型而感到困惑,尽管在教授继承时使用“is-a”短语并不是真的Customer“is-a”Type。当然,相关的混淆还包括静态(编译时)和动态(运行时)类型之间的区别。示例暗示了这一点,但我不想明确提到它。 - phoog
确实!(同意,“将类型作为对象传递”更清晰)。这很难解释,你的代码和上面的解释都非常棒! - David E
@DavidEdey 更明确一点,我不知道,因为“作为一个对象”可能被理解为“作为一个object”,也就是说,“作为一个System.Object”,这并不是真正的重点。 - phoog
嗯,没错!我想这只是一个相当令人困惑的点……也许我们可以说“T是一种类型,并且可以在代码中替换另一种类型”,而"typeof(T)tInstance.getType()等则提供了一个表示类型T的对象"……或者甚至只是说"typeof(T) 就是 一个对象"。不确定这是否更清晰哈哈。抱歉这篇文章有点毫无意义!哈哈 - David E

1
似乎“正确”的实现方式是去除Type约束:
public class Service {
    public void Fn<T>(): where T : I, new() {
        ( new T() ).M();
        // ...
    }
}

public class Main {
    public void main() {
        Service.Fn<C>();
        Service.Fn<D>();
        // ...
    }
}

如果您真的非常想传递一个类型,那么您将不得不使用反射而不是泛型:
public class Service {
    public void Fn<T>(Type t) {
        I i = Activator.CreateInstance(t) as I;
        if(i != null)
            i.M();
        // ...
    }
}

问题在于你不能在编译时强制要求 t 是一个同时实现了 I 接口的类型。

实际上,即使这不是人们想要做的事情,你仍然可以从类型继承。 - phoog
我会使用你的第一个版本,因为它更简洁/易于使用。 - xst

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