Consider I have a TemplateEngine with the three classes:
TemplateEngine
TemplateBase<T> where T : TemplateDataBase
TemplateDataBase
and the sample implementation:
SampleTemplate : TemplateBase<SampleTemplateData>
SampleTemplateData : TemplateDataBase
and now, in the engine, a getter function:
public T GetTemplate<T, U>() where T: TemplateBase<U>, new() where U : TemplateDataBase
{
var template = new T();
...
return template;
}
Since all implementations of TemplateBase will have exactly one valid value for U, like the sample, the type of U can be infered through the choice of T, and I shouldn't have to provide it to the GetTemplate method.
Other TemplateData classes contain completely different data and one shouldn't be able to use the wrong TemplateData class for a certain template.
If I now remove the U type parameter from the function call, I get "Incorrect number of type parameters", or, if I remove it from the function definition, the getter is no longer valid because "Cannot resolve U".
If I keep the parameter, I still am not allowed to do so because "There is no implicit reference conversion from SampleTemplate to TemplateBase<TemplateDataBase>".
What am I doing wrong here?
Since you are trying to use a type parameter that is a child of the type parameter defined in the GetTemplate method, you'll need to use a covariant type parameter. Which by definition
Enables you to use a more derived type than originally specified
And since variance modifies can only be applied to interfaces or delegates, you'll need to create one of the two. Here's an example of using a generic interface with a covariant type parameter that allows you the type parameter to be implied:
interface ITemplate<out T> where T : TemplateDataBase
{
Type DataType { get; }
}
class TemplateBase<T> : ITemplate<T> where T : TemplateDataBase
{
public Type DataType => typeof(T);
}
class TemplateDataBase { }
class TemplateEngine
{
public T GetTemplate<T>() where T : ITemplate<TemplateDataBase>, new()
{
var template = new T();
return template;
}
}
class SampleTemplate : TemplateBase<SampleTemplateData> { }
class SampleTemplateData : TemplateDataBase { }
Note the ITemplate<out T>. This is what actually says that the type parameter is covariant.
And here's an example of the use site where the type is being inferred:
static void Main(string[] args)
{
var engine = new TemplateEngine();
var sampleTemplate = engine.GetTemplate<SampleTemplate>();
Console.WriteLine($"{sampleTemplate.DataType.Name}");
Console.ReadLine();
}
Related
I want to restrict a generic type to be of particular type. In this case, i want the generic type T to be of something that is IComparable. And i want the restriction to happen at parent class level(or parent interface level). Here is sample code:
abstract class BaseContainer<T> where T : IComparable<T> {
protected List<T> container;
}
class QueueCustom<T> : BaseContainer<T> {
public QueueCustom() {
this.container = new List<T>();
}
}
This throws a error:
The type 'T' cannot be used as type parameter 'T' in the generic type
or method 'BaseContainer'. There is no boxing conversion or type
parameter conversion from 'T' to 'System.IComparable'.
I can restrict the type T at the child class level though:
abstract class BaseContainer<T> {
protected List<T> container;
}
class QueueCustom<T> : BaseContainer<T> where T : IComparable<T> {
public QueueCustom() {
this.container = new List<T>();
}
}
How do i go about doing it at parent class and not at the derived class?
You can do it in both.
By using class QueueCustom : BaseContainer, You declare a statement saying "The type chosen for derived class will be base class's type as well".
This means that, in order to match base class's requirements, you have to demand them as well.
I have code like this:
class Base { }
class Derived : Base { }
class Wrapper<T> {
public T Value { get; }
public Wrapper (T value) { Value = value; }
}
I would like to use Wrapper like this:
Wrapper<Base> wrapper = new Wrapper<Derived> (new Derived ());
But it ends up with this error:
Error CS0029 Cannot implicitly convert type 'Wrapper<Derived>' to 'Wrapper<Base>'
I tried creating method in Wrapper class that would act as converter
public Wrapper<TResult> To<TResult> () /* Constraints needed here. */ =>
new Wrapper<TResult> (Value);
But I'm missing some valid constraints. Current code ends up with error:
S1503 Argument 1: cannot convert from 'T' to 'TResult'
I would imagine constraints on To method could look like where T : TResult, but that's not valid constraints.
Any ways to implement converter from Wrapper<Derived> to Wrapper<Base> easily?
You could use covariance like so:
class Base { }
class Derived : Base { }
interface IWrapper<out T>
{
T Value { get; }
}
class Wrapper<T> : IWrapper<T>
{
public T Value { get; private set; }
public Wrapper(T value) { Value = value; }
}
class Program
{
static void Main(string[] args)
{
IWrapper<Base> wrapper = new Wrapper<Derived>(new Derived());
}
}
At first I would add a constraint to the class demanding that T must be of type Base:
class Base { }
class Derived : Base { }
class Wrapper<T> where T : Base // T must be (derived from) Base
{
public T Value { get; }
public Wrapper (T value) { Value = value; }
}
Secondly, a generic converter would be dangerous. What if someone tries to convert a Wrapper<Gnu> to a Wrapper<Lion>?
So I'd take a step back and make a non-generic converter that simply converts to Wrapper<Base>:
public Wrapper<Base> ToBase()
{
return new Wrapper<Base>(Value);
}
And this works because of the constraint for T at class level.
C# is actually a language known for a high level of type safety. But you can get around it and do what you asked for in the comment by ommitting any constraints and just trying to cast whatever comes in:
public Wrapper<TResult> To<TResult>() where TResult : class
{
return new Wrapper<TResult>(Value as TResult);
}
You need the class constraint and the as operator because a direct cast between two generic parameters is not compilable (as the IL depends too much on the specific types).
But this will return Wrapper instances with Value set to null if the types don't match. And it will work with derived types instead of base types too. So take care. You may add some extra checks for that. And take care of the gnus :)
UPDATE:
A safer way:
public Wrapper<TResult> To<TResult>() where TResult : class// TResult must also be (derived from) Base
{
if (!typeof(TResult).IsAssignableFrom(typeof(T)))
throw new InvalidCastException();
return new Wrapper<TResult>(Value as TResult);
}
This checks that T is derived from TResult and throws an InvalidCastException if not. You may refine that for your needs.
The problem you're encountering is that the generic types Wrapper<Base> and Wrapper<Derived> are two completely different classes for the .NET Framework
What you could do is creating a new Wrapper of type Base:
Wrapper<Base> wrapper = new Wrapper<Base>(new Derived());
Or to complete your To-method approach:
public Wrapper<TResult> To<TResult>() where TResult : T
=> new Wrapper<TResult>( (TResult)Value ); // This could throw an error
public bool TryCastTo<TResult>(out Wrapper<TResult> derivedWrapper) where TResult : T
{
derivedWrapper = null;
// EDIT: changed to the version from René Vogt since this is much cleaner and mine had a little error
if (!typeof(T).IsAssignableFrom(typeof(TResult)))
{
return false;
}
derivedWrapper = new Wrapper<TResult>( (TResult)Value );
return true;
}
The usage would be:
Wrapper<Derived> derivedWrapper1 = wrapper.To<Derived>();
Wrapper<Derived> derivedWrapper2;
bool success = wrapper.TryCastTo<Derived>(out derivedWrapper2);
I have an app written in C#. My app has a class that looks like the following:
public class FinderA
{
public IEnumerable<FinderA> GetItems()
{
return FinderA.FindAll();
}
}
I want to require other classes to do something similar. I cannot use a base class because my actual implementation is already using a base class. For that reason, I want to create an interface. Currently, I'm trying the following:
public interface IFinder
{
IEnumerable<T> GetItems();
}
When I use this approach, I get a compile-time error that says: "The type or namespace name 'T' could not be found (are you missing...". To overcome this, I add <T> to the end of the interface name so it looks like this:
public interface IFinder<T>
{
IEnumerable<T> GetItems();
}
This is turn generates another compile-time error that says: "Using the generic type 'IFinder' requires 1 type arguments.". My challenge is, I want the interface to be generic. I do not want to pass in a type. Is there a way around this? If so, what/how?
There is no way around this; you'll need to actually supply the generic type argument when declaring that a class implements the interface.
You can do this at the method level instead of as a generic type on the interface itself.
public interface IFinder
{
IEnumerable<T> GetItems<T>();
}
Your code can then call it like such:
IFinder finder = // Create finder instance
IEnumerable<MyClass> discoveredClasses = finder.GetItems<MyClass>();
If you want to ensure that MyClass is a class that implements IFinder, you can constrain the method.
public interface IFinder
{
IEnumerable<T> GetItems<T>() where T : IFinder;
}
That will cause the following to generate a compiler error:
public class Foo
{
}
public class Bar
{
Bar()
{
IFinder finder = // Create finder.
// This fails because <T> (Foo) does not implement IFinder.
IEnumerable<Foo> fooItems = finder.GetItems<Foo>();
}
}
but it will allow the following to compile
public class MyFinderA : IFinder
{
IEnumerable<T> GetItems<T>() where T : IFinder
{
return // Create collection of T
}
public class Bar
{
Bar()
{
IFinder finder = // Create finder.
// This works as <T> (MyFinderA) is an IFinder implementation
IEnumerable<MyFinderA> finderItems = finder.GetItems<MyFinderA>();
}
}
If you want your interface to be generic but you are not willing to supply a type argument, you can return an IEnumerable<object>.
public interface IFinder {
IEnumerable<object> GetItems();
}
All class types extend from System.Object so that should suffice for any type in your applicacion (enums and structs would get boxed)
Please note that this approach requires the interface's consumer to cast the generic object instances to the appropiate types.
The alternate (and recommended) approach is to make the interface use type parameters, but for that you must supply the appropiate type arguments
Suppose I have a generic method that is made generic just for the purpose of returning correct type so upstream callers don't have to cast returned values.
public T Add<T>(string name, string details, ...)
where T: BaseClass
{
// somehow get correct ObjectType
ObjectType type = ??????;
T result = Repo.Add<T>(type, name, details, ...);
...
return result;
}
Problem
The problem of this generic method is that I'm not getting an instance of a concrete class represented with generic type. This means that callers of this method have to explicitly provide generic type as type inference can't be done.
public abstract class BaseClass
{
public abstract ObjectType ActualType { get; }
...
}
Implemented child classes define this property as a quasy constant
public class ConcreteClass: BaseClass
{
public override ObjectType ActualType
{
get { return ObjectType.SomeType; }
}
...
}
Question
Based on generic method call
var result = Add<ConcreteClass>("title", "details");
how can I get the value of ActualType within my Add<T> method? I also tried adding new() generic type constraint, but that doesn't compile as my BaseClass is abstract, so I'm unable to call
new T();
within my method to get that ActualType value.
You need to apply the new() constraint to your Add method like this:
public T Add<T>(string name, string details)
where T: BaseClass, new()
{
T result = new T();
//snip
return result;
}
public abstract class BaseClass { /* snip */ }
public class ConcreteClass: BaseClass { /* snip */ }
Which means this code will work:
var thing = Add<ConcreteClass>("Fred", "Lives in France");
According to the MSDN documentation on the new constraint:
The new constraint specifies that any type argument in a generic class
declaration must have a public parameterless constructor. To use the
new constraint, the type cannot be abstract.
This refers to the type passed in, not the other constraint on your method.
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?
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.
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 constructor
class - Means that the generic type placeholder must be a reference type
struct - 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.
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
}
}
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
}
}