Reputation: 5714
In C++, you can invoke method's from a template argument like so:
template<class T> class foo
{
T t;
t.foo();
}
But in C#, it looks like this is not possible:
class foo<T>
{
T t;
public void foo() {
t.foo(); // Generates a compiler error
}
};
I suppose this probably isn't possible in C#, is it?
Upvotes: 3
Views: 253
Reputation: 659956
You have discovered the difference between templates and generics. Though they look similar they are in fact quite different.
A template need be correct only for the type arguments that are actually provided; if you provide a T that does not have a method foo then the compilation fails; if you provide only type arguments that have a foo then compilation succeeds.
By contrast a generic must be correct for any possible T. Since we have no evidence that every possible T will have a method foo then the generic is illegal.
Upvotes: 11
Reputation: 38397
Yes, if you know that the generic type placeholder T
implements a member from a base class or interface, you can constrain the type T
to that base class or interface using a where
clause.
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.
}
}
This enables the generic type placeholder T
to be treated as an IFooable
. If you do not constrain a generic type placeholder in a generic, then it is constrained to object
which means only object
's members are visible to the generic (that is, you only see members visible to an object
reference, but calling any overridden members will call the appropriate override).
Note: This is additionally important because of things like operator overloading (remember that operators are overloaded, not overridden) so if you had code like this:
public bool SomeSuperEqualsChecker<T>(T one, T two)
{
return one == two;
}
This will always use object
's ==
even if T
is string
. However, if we had:
public bool SomeSuperEqualsChecker<T>(T one, T two)
{
// assume proper null checking exists...
return one.Equals(two);
}
This WOULD work as expected with string
because Equals()
is overridden, not overloaded.
So, the long and the short is just remember that an unconstrained generic placeholder does represent any type, but the only calls and operations visible are those visible on object
.
In addition to interface/base class constraints, there are a few other constraints:
new()
- Means that the generic type placeholder must have a default constructorclass
- Means that the generic type placeholder must be a reference typestruct
- Means that the generic type placeholder must be a value type (enum, primitive, struct, etc)For example:
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)
}
Hope this helps.
Upvotes: 9
Reputation: 24167
It is possible if you are willing to accept generic type constraints. This means that your generic type must be constrained to derive from some base class or implement some interface(s).
Example:
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
}
}
Upvotes: 3
Reputation: 22433
You need to add a type constraint to your method.
public interface IFoo {
void Foo();
}
public class Foo<T> where T : IFoo {
T t;
public void foo() {
t.Foo(); // Generates a compiler error
}
}
Upvotes: 3