在C ++中,您可以从模板参数中调用方法,如下所示:
template<class T> class foo
{
T t;
t.foo();
}
但是在C#中,看起来这是不可能的:
class foo<T>
{
T t;
public void foo() {
t.foo(); // Generates a compiler error
}
};
我想这可能在C#中是不可能的,是吗?
答案 0 :(得分:11)
您已发现 templates 和 generics 之间的区别。虽然它们看起来很相似但实际上却完全不同。
模板只需要对实际提供的类型参数正确;如果你提供一个没有方法foo的T,那么编译失败;如果只提供具有foo的类型参数,则编译成功。
相比之下,通用必须对任何可能的T 都是正确的。由于我们没有证据表明每个可能的T 都有方法foo,因此通用是非法的。
答案 1 :(得分:9)
是的,如果您知道泛型类型占位符T
实现了基类或接口的成员,则可以使用{{1}将类型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.
}
}
视为T
。如果不约束泛型中的泛型类型占位符,则它被约束为IFooable
,这意味着只有object
的成员对泛型可见(即,您只看到object
引用可见的成员,但调用任何被覆盖的成员将调用适当的覆盖。)
注意:这是非常重要的,因为运算符重载之类的东西(记住运算符是重载的,而不是重写的)所以如果你有这样的代码:
object
即使public bool SomeSuperEqualsChecker<T>(T one, T two)
{
return one == two;
}
为object
,也会始终使用==
的{{1}}。但是,如果我们有:
T
此WOULD按预期与string
一起工作,因为public bool SomeSuperEqualsChecker<T>(T one, T two)
{
// assume proper null checking exists...
return one.Equals(two);
}
被覆盖,而不是重载。
因此,长和短只是记住一个不受约束的通用占位符确实代表任何类型,但唯一可见的调用和操作是string
上可见的。
除了接口/基类约束之外,还有一些其他约束:
Equals()
- 表示泛型类型占位符必须具有默认构造函数object
- 表示通用类型占位符必须是引用类型new()
- 表示泛型类型占位符必须是值类型(枚举,原语,结构等)例如:
class
希望这有帮助。
答案 2 :(得分:3)
您需要在方法中添加type constraint。
public interface IFoo {
void Foo();
}
public class Foo<T> where T : IFoo {
T t;
public void foo() {
t.Foo(); // Generates a compiler error
}
}
答案 3 :(得分:3)
如果您愿意接受泛型类型约束,则可能。这意味着必须将通用类型约束为从某个基类派生或实现某些接口。
示例:
abstract class SomeBase
{
public abstract DoSomething();
}
// new() ensures that there is a default constructor to instantiate the class
class Foo<T> where T : SomeBase, new()
{
T t;
public Foo()
{
this.t = new T();
this.t.DoSomething(); // allowed because T must derive from SomeBase
}
}