在C++中,您可以从模板参数调用方法,如下所示:
template<class T> class foo
{
T t;
t.foo();
}
Run Code Online (Sandbox Code Playgroud)
但在C#中,看起来这是不可能的:
class foo<T>
{
T t;
public void foo() {
t.foo(); // Generates a compiler error
}
};
Run Code Online (Sandbox Code Playgroud)
我想这在C#中可能是不可能的,是吗?
Eri*_*ert 11
您已经发现了模板和泛型之间的区别.虽然它们看起来很相似但实际上却完全不同.
模板只需要对实际提供的类型参数是正确的; 如果你提供一个没有方法foo的T,那么编译失败; 如果只提供具有foo的类型参数,则编译成功.
相比之下,通用必须对任何可能的T都是正确的.由于我们没有证据表明每个可能的T都有方法foo,因此通用是非法的.
是的,如果您知道泛型类型占位符T实现了基类或接口的成员,则可以T使用where子句将类型约束到该基类或接口.
public interface IFooable
{
void Foo();
}
// ...
public class Foo<T> where T : IFooable
{
private T _t;
// ...
public void DoFoo()
{
_t.Foo(); // works because we constrain T to IFooable.
}
}
Run Code Online (Sandbox Code Playgroud)
这使得泛型类型占位符T可以被视为IFooable.如果你没有约束泛型中的泛型类型占位符,那么它被约束到object只有那些object成员对泛型可见(也就是说,你只看到object引用可见的成员,但调用任何被覆盖的成员将调用适当的覆盖).
注意:这是另外重要的,因为像运算符重载(请记住运算符被重载,而不是重写),所以如果您有这样的代码:
public bool SomeSuperEqualsChecker<T>(T one, T two)
{
return one == two;
}
Run Code Online (Sandbox Code Playgroud)
这将始终使用object的==即使T是string.但是,如果我们有:
public bool SomeSuperEqualsChecker<T>(T one, T two)
{
// assume proper null checking exists...
return one.Equals(two);
}
Run Code Online (Sandbox Code Playgroud)
这个WOULD按预期工作,string因为Equals()被覆盖,而不是超载.
因此,长和短只是记住一个不受约束的通用占位符确实代表任何类型,但唯一可见的调用和操作是可见的object.
除了接口/基类约束之外,还有一些其他约束:
new() - 表示泛型类型占位符必须具有默认构造函数class - 表示泛型类型占位符必须是引用类型struct - 表示泛型类型占位符必须是值类型(枚举,原语,结构等)例如:
public class Foo<T> where T : new()
{
private T _t = new T(); // can only construct T if have new() constraint
}
public class ValueFoo<T> where T : struct
{
private T? _t; // to use nullable, T must be value type, constrains with struct
}
public class RefFoo<T> where T : class
{
private T _t = null; // can only assign type T to null if ref (or nullable val)
}
Run Code Online (Sandbox Code Playgroud)
希望这可以帮助.