Generic class with method taking a different generic class - c#

Sorry for asking what seems like such an obvious question.
I have an "adapter" generic class of type T where T is a defined interface. I want to create a method in that class which takes a different type of generic adapter.
public interface ICommon { }
public class TypeOne : ICommon { }
public class TypeTwo : ICommon { }
public class Adapter<T> where T : ICommon
{
void TakeAnotherType(Adapter<S> other)
{ }
}
This gives me a compilation error Cannot resolve symbol 'S'.
I would like to be able to do;
MyOne one = new Adapter<TypeOne>();
MyTwo two = new Adapter<TypeTwo>();
one.TakeAnotherType(two);
If I change the method to take Adapter<T> then it complains that "two" isn't of type Adapter<TypeOne>

void TakeAnotherType<S>(Adapter<S> other) where S:ICommon
{ }
Because the generic type T of Adapter is constrained to ICommon, it is important to also constrain S in the same way, as this is used as the generic type for Adapter<T>, so it must also satisfy the ICommon constraint.

Just change
void TakeAnotherType(Adapter<S> other)
into
void TakeAnotherType<S>(Adapter<S> other) where S : ICommon
and it should work.

Once you have specified that a class is generic (by naming it Something<T>), every method within it is implicitly generic on that type T. To say that a method is generic on a different type, you have to specify that fact in the method name, by calling it SomeMethod<U>. The body of this method will then have access to both types, T (the type that the class is generic on) and U (the class that the method is generic on).
Note also that the two usual conventions for type parameters are T, U, V etc; or TSomething, TSomethingElse, TEvenMore. I don't think I've ever seen S used...

Related

How to enforce interface generics' rules on inherited type? [duplicate]

Item class
public class Item
{
public bool Check(int value) { ... }
}
Base abstract class with generic type constraint
public abstract class ClassBase<TItem>
where TItem : Item
{
protected IList<TItem> items;
public ClassBase(IEnumerable<TItem> items)
{
this.items = items.ToList();
}
public abstract bool CheckAll(int value);
}
Inherited class without constraints
public class MyClass<TItem> : ClassBase<TItem>
{
public override bool CheckAll(int value)
{
bool result = true;
foreach(TItem item in this.items)
{
if (!item.Check(value)) // this doesn't work
{
result = false;
break;
}
}
return result;
}
}
I would like to know why aren't generic type constraints inheritable? Because if my inherited class inherits from base class and passes over its generic type which has a constraint on the base class it automatically means that generic type in inherited class should have the same constraint without explicitly defining it. Shouldn't it?
Am I doing something wrong, understanding it wrong or is it really that generic type constraint aren't inheritable? If the latter is true, why in the world is that?
A bit of additional explanation
Why do I think that generic type constraints defined on a class should be inherited or enforced on child classes? Let me give you some additional code to make it bit less obvious.
Suppose that we have all three classes as per above. Then we also have this class:
public class DanteItem
{
public string ConvertHellLevel(int value) { ... }
}
As we can see this class does not inherit from Item so it can't be used as a concrete class as ClassBase<DanteItem> (forget the fact that ClassBase is abstract for now. It could as well be a regular class). Since MyClass doesn't define any constraints for its generic type it seems perfectly valid to have MyClass<DanteItem>...
But. This is why I think generic type constraints should be inherited/enforced on inherited classes just as with member generic type constraints because if we look at definition of MyClass it says:
MyClass<T> : ClassBase<T>
When T is DanteItem we can see that it automatically can't be used with MyClass because it's inherited from ClassBase<T> and DanteItem doesn't fulfill its generic type constraint. I could say that **generic type on MyClass depends on ClassBase generic type constraints because otherwise MyClass could be instantiated with any type. But we know it can't be.
It would be of course different when I would have MyClass defined as:
public class MyClass<T> : ClassBase<Item>
in this case T doesn't have anything to to with base class' generic type so it's independent from it.
This is all a bit long explanation/reasoning. I could simply sum it up by:
If we don't provide generic type constraint on MyClass it implicitly implies that we can instantiate MyClass with any concrete type. But we know that's not possible, since MyClass is inherited from ClassBase and that one has a generic type constraint.
I hope this makes much more sense now.
ANOTHER UPDATE:
This question was the subject of my blog in July 2013. Thanks for the great question!
UPDATE:
I've given this some more thought and I think the problem is that you don't want inheritance at all. Rather, what you want is for all constraints that must be placed on a type parameter in order for that type parameter to be used as a type argument in another type to be automatically deduced and invisibly added to the declaration of the type parameter. Yes?
Some simplified examples:
class B<T> where T:C {}
class D<U> : B<U> {}
U is a type parameter that is used in a context where it must be C. Therefore in your opinion the compiler should deduce that and automatically put a constraint of C on U.
What about this?
class B<T, U> where T : X where U : Y {}
class D<V> : B<V, V> {}
Now V is a type parameter used in a context where it must be both X and Y. Therefore in your opinion the compiler should deduce that and automatically put a constraint of X and Y on V. Yes?
What about this?
class B<T> where T : C<T> {}
class C<U> : B<D<U>> where U : IY<C<U>> {}
class D<V> : C<B<V>> where V : IZ<V> {}
I just made that up, but I assure you that it is a perfectly legal type hierarchy. Please describe a clear and consistent rule that does not go into infinite loops for determining what all the constraints are on T, U and V. Don't forget to handle the cases where type parameters are known to be reference types and the interface constraints have covariance or contravariance annotations! Also, the algorithm must have the property that it gives exactly the same results no matter what order B, C and D appear in source code.
If inference of constraints is the feature you want then the compiler has to be able to handle cases like this and give clear error messages when it cannot.
What is so special about base types? Why not actually implement the feature all the way?
class B<T> where T : X {}
class D<V> { B<V> bv; }
V is a type parameter used in a context where it must be convertible to X; therefore the compiler should deduce this fact and put a constraint of X on V. Yes? Or no?
Why are fields special? What about this:
class B<T> { static public void M<U>(ref U u) where U : T {} }
class D<V> : B<int> { static V v; static public void Q() { M(ref v); } }
V is a type parameter used in a context where it can only be int. Therefore the C# compiler should deduce this fact and automatically put a constraint of int on V.
Yes? No?
You see where this is going? Where does it stop? In order to implement your desired feature properly the compiler must do whole-program analysis.
The compiler does not do this level of analysis because that is putting the cart before the horse. When you construct a generic, you are required to prove to the compiler that you've satisfied the constraint. It's not the compiler's job to figure out what you meant to say and work out what further set of constraints satisfy the original constraint.
For similar reasons, the compiler also does not attempt to automatically infer variance annotations in interfaces on your behalf. See my article on that subject for details.
http://blogs.msdn.com/b/ericlippert/archive/2007/10/29/covariance-and-contravariance-in-c-part-seven-why-do-we-need-a-syntax-at-all.aspx
Original answer:
I would like to know why aren't generic type constraints inheritable?
Only members are inherited. A constraint is not a member.
if my inherited class inherits from base class and passes over its generic type which has a constraint on the base class it automatically means that generic type in inherited class should have the same constraint without explicitly defining it. Shouldn't it?
You're just asserting how something should be, without providing any explanation of why it should be that way. Explain to us why you believe that the world should be that way; what are the benefits and what are the drawbacks and what are the costs?
Am I doing something wrong, understanding it wrong or is it really that generic type constraint aren't inheritable?
Generic constraints are not inherited.
If the latter is true, why in the world is that?
Features are "not implemented" by default. We don't have to provide a reason why a feature is not implemented! Every feature is not implemented until someone spends the money to implement it.
Now, I hasten to note that generic type constraints are inherited on methods. Methods are members, members are inherited, and the constraint is a part of the method (though not part of its signature). So the constraint comes along with the method when it is inherited. When you say:
class B<T>
{
public virtual void M<U>() where U : T {}
}
class D<V> : B<IEnumerable<V>>
{
public override void M<U>() {}
}
Then D<V>.M<U> inherits the constraint and substitutes IEnumerable<V> for T; thus the constraint is that U must be convertible to IEnumerable<V>. Note that C# does not allow you to restate the constraint. This is in my opinion a misfeature; I would like to be able to restate the constraint for clarity.
But D does not inherit any kind of constraint on T from B; I don't understand how it possibly could. M is a member of B, and is inherited by D along with its constraint. But T is not a member of B in the first place, so what is there to inherit?
I'm really not understanding at all what feature it is that you want here. Can you explain with more details?
Below is a scenario where the implicit nature of this behavior causes different behavior than expected:
I recognize that this scenario may seem extravagant in the amount of setup, but this is just one example of where this behavior might cause a problem. Software applications can be complicated, so even though this scenario may seem complicated, I wouldn't say that this can't happen.
In this example there is an Operator class that implements two similar interfaces: IMonitor and IProcessor. Both have a start method and an IsStarted property, but the behavior for each interface within the Operator class is separate. I.e. there is a _MonitorStarted variable and a _ProcessorStarted variable within the Operator class.
MyClass<T> derives from ClassBase<T>. ClassBase has a type constraint on T that it must implement the IProcessor interface, and according to the suggested behavior MyClass inherits that type constraint.
MyClass<T> has a Check method, which is built with the assumption that it can get the value of the IProcessor.IsStarted property from the inner IProcessor object.
Suppose someone changes the implementation of ClassBase to remove the type constraint of IProcessor on the generic parameter T and replace it with a type contraint of IMonitor. This code will silently work, but will produce different behavior. The reason is because the Check method in MyClass<T> is now calling the IMonitor.IsStarted property instead of the IProcessor.IsStarted property, even though the code for MyClass<T> hasn't changed at all.
public interface IMonitor
{
void Start();
bool IsStarted { get; }
}
public interface IProcessor
{
void Start();
bool IsStarted { get; }
}
public class Operator : IMonitor, IProcessor
{
#region IMonitor Members
bool _MonitorStarted;
void IMonitor.Start()
{
Console.WriteLine("IMonitor.Start");
_MonitorStarted = true;
}
bool IMonitor.IsStarted
{
get { return _MonitorStarted; }
}
#endregion
#region IProcessor Members
bool _ProcessorStarted;
void IProcessor.Start()
{
Console.WriteLine("IProcessor.Start");
_ProcessorStarted = true;
}
bool IProcessor.IsStarted
{
get { return _ProcessorStarted; }
}
#endregion
}
public class ClassBase<T>
where T : IProcessor
{
protected T Inner { get; private set; }
public ClassBase(T inner)
{
this.Inner = inner;
}
public void Start()
{
this.Inner.Start();
}
}
public class MyClass<T> : ClassBase<T>
//where T : IProcessor
{
public MyClass(T inner) : base(inner) { }
public bool Check()
{
// this code was written assuming that it is calling IProcessor.IsStarted
return this.Inner.IsStarted;
}
}
public static class Extensions
{
public static void StartMonitoring(this IMonitor monitor)
{
monitor.Start();
}
public static void StartProcessing(this IProcessor processor)
{
processor.Start();
}
}
class Program
{
static void Main(string[] args)
{
var #operator = new Operator();
#operator.StartMonitoring();
var myClass = new MyClass<Operator>(#operator);
var result = myClass.Check();
// the value of result will be false if the type constraint on T in ClassBase<T> is where T : IProcessor
// the value of result will be true if the type constraint on T in ClassBase<T> is where T : IMonitor
}
}
I think you're confused becuase you're declaring you derived class with TItem as well.
If you think about it if you were using Q instead so.
public class MyClass<Q> : BaseClass<Q>
{
...
}
Then how is it to be determined that Q is of the type item?
You need to add the constraint to the derived classes Generic Type as well so
public class MyClass<Q> : BaseClass<Q> were Q : Item { ... }
Because the ClassBase has a constraint on his template (should by typeof Item), you have to add this constraint to MyClass too.
If you don't do this, you could create a new instance of MyClass, where the template isn't a type of Item. When creating the base class, it will fail.
[edit]
Hmm now a re-read your question, and I see your code does compile? Ok.
Well, im MyClass you don't know the basetype of this.items, so you can't call the Check method.
this.items is of the type IList, and in your class, TItem isn't specified, thats why the class doesn't understand the Check method.
Let me counter your question, why don't you want to add the constraint to your MyClass class? Given any other class type as template to this class, would result in an error. Why not prevent this errors by adding a constraint so it will fail compiletime.

Chose generic implementation if the type parameter is struct or class

I would like to implement my generic IQueue<T> interface in an efficient way by doing one implementation if T is struct and another if T is a class.
interface IQueue<T> { ... }
class StructQueue<T> : IQueue<T> where T : struct { ... }
class RefQueue<T> : IQueue<T> where T : class { ... }
The, I'd like to have a factory method which based on T's kind returns an instance of one or the other:
static IQueue<T> CreateQueue<T>() {
if (typeof(T).IsValueType) {
return new StructQueue<T>();
}
return new RefQueue<T>();
}
Of course, the compiler indicates that T should be non-nullable/nullable type argument respectively.
Is there a way to cast T into a struct kind (and into a class kind) to make the method compile? Is this kind of runtime dispatching even possible with C#?
You can use Reflection to do it like this:
static IQueue<T> CreateQueue<T>()
{
if (typeof(T).IsValueType)
{
return (IQueue<T>)Activator
.CreateInstance(typeof(StructQueue<>).MakeGenericType(typeof(T)));
}
return (IQueue<T>)Activator
.CreateInstance(typeof(RefQueue<>).MakeGenericType(typeof(T)));
}
This code uses the Activator.CreateInstance method to create queues at runtime. This method takes in the type of the object you want to create.
To create a Type that represents the generic class, this code uses the MakeGenericType method to create a closed generic Type object from the open generic types like StructQueue<>.
Yacoub Massad's answer is correct, but with a little modification, you don't need to run MakeGenericType for each call to CreateQueue.
The code below runs MakeGenericType once per type, since a separate static variable exists for each type of QueueFactory<T>, i.e QueueFactory<int>.queueType will get StructQueue<int>, while QueueFactory<string>.queueType will get RefQueue<int>
public class QueueFactory<T>
{
static Type queueType = typeof(T).IsValueType ?
typeof(StructQueue<>).MakeGenericType(typeof(T)) : typeof(RefQueue<>).MakeGenericType(typeof(T));
public static IQueue<T> CreateQueue()
{
return (IQueue<T>)Activator.CreateInstance(queueType);
}
}
In my semi-scientific test, it created 1 million instances in about a tenth of the time.

Explicit generic method implementation with 2 Type parameters -- only explicitly name 1 Type Parameter

I have an interface that has a generic method with two type parameters. I want to partially explicitly implement that generic method in a class. Is this possible? Some example code below:
public interface ISomeInterface
{
TResultType Results<TResultsType,TSearchCriteriaType>(TSearchCriteriaType searchCriteria);
}
public class SomeConcrete : ISomeInterface
{
public TResultsType Results<TResultsType, ConcreteSearchCriteria>(ConcreteSearchCriteria searchCriteria)
{
return (TResultsType)Results;
}
}
Do I have to explicitly implement both type parameters to make this work?
Do I have to explicitly implement both type parameters to make this work?
In order to implement this interface, your class must allow ANY types to be used for that method. (Any types which fit the constraints defined in the interface, which, in this case, since there are no constraints, means any type.)
You can't restrict the interface within a specific class implementing it, since this is a generic method (not a generic type), and there is no constraints which cause this to work properly.
In order to do what you wish, I think, you'd need to make the interface generic:
public interface ISomeInterface<TSearchCriteriaType>
{
TResultType Results<TResultsType>(TSearchCriteriaType searchCriteria);
}
You can then implement it as:
public class SomeConcrete : ISomeInterface<ConcreteSearchCriteria>
{
public TResultsType Results<TResultsType>(ConcreteSearchCriteria searchCriteria)
{
var results = GenerateResults();
return (TResultsType)results;
}
}
By making the interface generic on the search criteria, you allow your class to implement it based on a specific type for the search criteria.
I think this is what you're looking for:
public class SomeConcrete : ISomeInterface
{
TResultsType ISomeInterface.Results<TResultsType, TSearchCriteriaType>(TSearchCriteriaType searchCriteria)
{
return Results<TResultsType>((ConcreteSearchCriteria)(object)searchCriteria);
}
public TResultsType Results<TResultsType>(ConcreteSearchCriteria searchCriteria)
{
// return something
}
}
Note that this will fail at run time, though the compiler can see no problems:
ISomeInterface someInterface = new SomeConcrete();
var result = someInterface.Results<object, SomeOtherSearchCriteria>(null);
More generally, you're no longer implementing the interface as might be expected. You might want to make it ISomeInterface<TResultsType, TSearchCriteriaType>, then make SomeConcrete<TResultsType> : ISomeInterface<TResultsType, ConcreteSearchCriteria>. That way you are implementing the interface as expected, declaring it all strongly.
You can't specialize a generic method like that. If it's generic in the interface, it has to be generic in the implementing classes. If you move the type parameters to the interface itself, then you can do it (and you can also have variance annotations, making it covariant in the result type and contravariant in the search criteria type):
public interface ISomeInterface<out TResultsType, in TSearchCriteriaType> {
TResultsType Results(TSearchCriteriaType searchCriteria);
}
public class SomeConcrete<TResultsType> :
ISomeInterface<TResultsType, ConcreteSearchCriteria> {
public TResultsType Results(ConcreteSearchCriteria searchCriteria) {
...
}
}
Now your class is generic on the result type.
You can also have a class specialized in a certain result type, and generic in the criteria type:
public class IntSearcher<TSearchCriteriaType> :
ISomeInterface<int, TSearchCriteriaType> {
public int Results(TSearchCriteriaType searchCriteria) {
...
}
}
I think this is a more consistent design, since both generic parameters are at the same level (and it looks like they should go together). But only you can say whether this is better for your code or not.
Yes, I think you do, otherwise you are not directly implementing the interface.
Your Implementation is not as generic as the interface contract.

Why aren't generic type constraints inheritable/hierarchically enforced

Item class
public class Item
{
public bool Check(int value) { ... }
}
Base abstract class with generic type constraint
public abstract class ClassBase<TItem>
where TItem : Item
{
protected IList<TItem> items;
public ClassBase(IEnumerable<TItem> items)
{
this.items = items.ToList();
}
public abstract bool CheckAll(int value);
}
Inherited class without constraints
public class MyClass<TItem> : ClassBase<TItem>
{
public override bool CheckAll(int value)
{
bool result = true;
foreach(TItem item in this.items)
{
if (!item.Check(value)) // this doesn't work
{
result = false;
break;
}
}
return result;
}
}
I would like to know why aren't generic type constraints inheritable? Because if my inherited class inherits from base class and passes over its generic type which has a constraint on the base class it automatically means that generic type in inherited class should have the same constraint without explicitly defining it. Shouldn't it?
Am I doing something wrong, understanding it wrong or is it really that generic type constraint aren't inheritable? If the latter is true, why in the world is that?
A bit of additional explanation
Why do I think that generic type constraints defined on a class should be inherited or enforced on child classes? Let me give you some additional code to make it bit less obvious.
Suppose that we have all three classes as per above. Then we also have this class:
public class DanteItem
{
public string ConvertHellLevel(int value) { ... }
}
As we can see this class does not inherit from Item so it can't be used as a concrete class as ClassBase<DanteItem> (forget the fact that ClassBase is abstract for now. It could as well be a regular class). Since MyClass doesn't define any constraints for its generic type it seems perfectly valid to have MyClass<DanteItem>...
But. This is why I think generic type constraints should be inherited/enforced on inherited classes just as with member generic type constraints because if we look at definition of MyClass it says:
MyClass<T> : ClassBase<T>
When T is DanteItem we can see that it automatically can't be used with MyClass because it's inherited from ClassBase<T> and DanteItem doesn't fulfill its generic type constraint. I could say that **generic type on MyClass depends on ClassBase generic type constraints because otherwise MyClass could be instantiated with any type. But we know it can't be.
It would be of course different when I would have MyClass defined as:
public class MyClass<T> : ClassBase<Item>
in this case T doesn't have anything to to with base class' generic type so it's independent from it.
This is all a bit long explanation/reasoning. I could simply sum it up by:
If we don't provide generic type constraint on MyClass it implicitly implies that we can instantiate MyClass with any concrete type. But we know that's not possible, since MyClass is inherited from ClassBase and that one has a generic type constraint.
I hope this makes much more sense now.
ANOTHER UPDATE:
This question was the subject of my blog in July 2013. Thanks for the great question!
UPDATE:
I've given this some more thought and I think the problem is that you don't want inheritance at all. Rather, what you want is for all constraints that must be placed on a type parameter in order for that type parameter to be used as a type argument in another type to be automatically deduced and invisibly added to the declaration of the type parameter. Yes?
Some simplified examples:
class B<T> where T:C {}
class D<U> : B<U> {}
U is a type parameter that is used in a context where it must be C. Therefore in your opinion the compiler should deduce that and automatically put a constraint of C on U.
What about this?
class B<T, U> where T : X where U : Y {}
class D<V> : B<V, V> {}
Now V is a type parameter used in a context where it must be both X and Y. Therefore in your opinion the compiler should deduce that and automatically put a constraint of X and Y on V. Yes?
What about this?
class B<T> where T : C<T> {}
class C<U> : B<D<U>> where U : IY<C<U>> {}
class D<V> : C<B<V>> where V : IZ<V> {}
I just made that up, but I assure you that it is a perfectly legal type hierarchy. Please describe a clear and consistent rule that does not go into infinite loops for determining what all the constraints are on T, U and V. Don't forget to handle the cases where type parameters are known to be reference types and the interface constraints have covariance or contravariance annotations! Also, the algorithm must have the property that it gives exactly the same results no matter what order B, C and D appear in source code.
If inference of constraints is the feature you want then the compiler has to be able to handle cases like this and give clear error messages when it cannot.
What is so special about base types? Why not actually implement the feature all the way?
class B<T> where T : X {}
class D<V> { B<V> bv; }
V is a type parameter used in a context where it must be convertible to X; therefore the compiler should deduce this fact and put a constraint of X on V. Yes? Or no?
Why are fields special? What about this:
class B<T> { static public void M<U>(ref U u) where U : T {} }
class D<V> : B<int> { static V v; static public void Q() { M(ref v); } }
V is a type parameter used in a context where it can only be int. Therefore the C# compiler should deduce this fact and automatically put a constraint of int on V.
Yes? No?
You see where this is going? Where does it stop? In order to implement your desired feature properly the compiler must do whole-program analysis.
The compiler does not do this level of analysis because that is putting the cart before the horse. When you construct a generic, you are required to prove to the compiler that you've satisfied the constraint. It's not the compiler's job to figure out what you meant to say and work out what further set of constraints satisfy the original constraint.
For similar reasons, the compiler also does not attempt to automatically infer variance annotations in interfaces on your behalf. See my article on that subject for details.
http://blogs.msdn.com/b/ericlippert/archive/2007/10/29/covariance-and-contravariance-in-c-part-seven-why-do-we-need-a-syntax-at-all.aspx
Original answer:
I would like to know why aren't generic type constraints inheritable?
Only members are inherited. A constraint is not a member.
if my inherited class inherits from base class and passes over its generic type which has a constraint on the base class it automatically means that generic type in inherited class should have the same constraint without explicitly defining it. Shouldn't it?
You're just asserting how something should be, without providing any explanation of why it should be that way. Explain to us why you believe that the world should be that way; what are the benefits and what are the drawbacks and what are the costs?
Am I doing something wrong, understanding it wrong or is it really that generic type constraint aren't inheritable?
Generic constraints are not inherited.
If the latter is true, why in the world is that?
Features are "not implemented" by default. We don't have to provide a reason why a feature is not implemented! Every feature is not implemented until someone spends the money to implement it.
Now, I hasten to note that generic type constraints are inherited on methods. Methods are members, members are inherited, and the constraint is a part of the method (though not part of its signature). So the constraint comes along with the method when it is inherited. When you say:
class B<T>
{
public virtual void M<U>() where U : T {}
}
class D<V> : B<IEnumerable<V>>
{
public override void M<U>() {}
}
Then D<V>.M<U> inherits the constraint and substitutes IEnumerable<V> for T; thus the constraint is that U must be convertible to IEnumerable<V>. Note that C# does not allow you to restate the constraint. This is in my opinion a misfeature; I would like to be able to restate the constraint for clarity.
But D does not inherit any kind of constraint on T from B; I don't understand how it possibly could. M is a member of B, and is inherited by D along with its constraint. But T is not a member of B in the first place, so what is there to inherit?
I'm really not understanding at all what feature it is that you want here. Can you explain with more details?
Below is a scenario where the implicit nature of this behavior causes different behavior than expected:
I recognize that this scenario may seem extravagant in the amount of setup, but this is just one example of where this behavior might cause a problem. Software applications can be complicated, so even though this scenario may seem complicated, I wouldn't say that this can't happen.
In this example there is an Operator class that implements two similar interfaces: IMonitor and IProcessor. Both have a start method and an IsStarted property, but the behavior for each interface within the Operator class is separate. I.e. there is a _MonitorStarted variable and a _ProcessorStarted variable within the Operator class.
MyClass<T> derives from ClassBase<T>. ClassBase has a type constraint on T that it must implement the IProcessor interface, and according to the suggested behavior MyClass inherits that type constraint.
MyClass<T> has a Check method, which is built with the assumption that it can get the value of the IProcessor.IsStarted property from the inner IProcessor object.
Suppose someone changes the implementation of ClassBase to remove the type constraint of IProcessor on the generic parameter T and replace it with a type contraint of IMonitor. This code will silently work, but will produce different behavior. The reason is because the Check method in MyClass<T> is now calling the IMonitor.IsStarted property instead of the IProcessor.IsStarted property, even though the code for MyClass<T> hasn't changed at all.
public interface IMonitor
{
void Start();
bool IsStarted { get; }
}
public interface IProcessor
{
void Start();
bool IsStarted { get; }
}
public class Operator : IMonitor, IProcessor
{
#region IMonitor Members
bool _MonitorStarted;
void IMonitor.Start()
{
Console.WriteLine("IMonitor.Start");
_MonitorStarted = true;
}
bool IMonitor.IsStarted
{
get { return _MonitorStarted; }
}
#endregion
#region IProcessor Members
bool _ProcessorStarted;
void IProcessor.Start()
{
Console.WriteLine("IProcessor.Start");
_ProcessorStarted = true;
}
bool IProcessor.IsStarted
{
get { return _ProcessorStarted; }
}
#endregion
}
public class ClassBase<T>
where T : IProcessor
{
protected T Inner { get; private set; }
public ClassBase(T inner)
{
this.Inner = inner;
}
public void Start()
{
this.Inner.Start();
}
}
public class MyClass<T> : ClassBase<T>
//where T : IProcessor
{
public MyClass(T inner) : base(inner) { }
public bool Check()
{
// this code was written assuming that it is calling IProcessor.IsStarted
return this.Inner.IsStarted;
}
}
public static class Extensions
{
public static void StartMonitoring(this IMonitor monitor)
{
monitor.Start();
}
public static void StartProcessing(this IProcessor processor)
{
processor.Start();
}
}
class Program
{
static void Main(string[] args)
{
var #operator = new Operator();
#operator.StartMonitoring();
var myClass = new MyClass<Operator>(#operator);
var result = myClass.Check();
// the value of result will be false if the type constraint on T in ClassBase<T> is where T : IProcessor
// the value of result will be true if the type constraint on T in ClassBase<T> is where T : IMonitor
}
}
I think you're confused becuase you're declaring you derived class with TItem as well.
If you think about it if you were using Q instead so.
public class MyClass<Q> : BaseClass<Q>
{
...
}
Then how is it to be determined that Q is of the type item?
You need to add the constraint to the derived classes Generic Type as well so
public class MyClass<Q> : BaseClass<Q> were Q : Item { ... }
Because the ClassBase has a constraint on his template (should by typeof Item), you have to add this constraint to MyClass too.
If you don't do this, you could create a new instance of MyClass, where the template isn't a type of Item. When creating the base class, it will fail.
[edit]
Hmm now a re-read your question, and I see your code does compile? Ok.
Well, im MyClass you don't know the basetype of this.items, so you can't call the Check method.
this.items is of the type IList, and in your class, TItem isn't specified, thats why the class doesn't understand the Check method.
Let me counter your question, why don't you want to add the constraint to your MyClass class? Given any other class type as template to this class, would result in an error. Why not prevent this errors by adding a constraint so it will fail compiletime.

A generic method can use contravariant/covariant types?

I'm writting a generalized method to use it in a special task at a T4 template. The method should allow me to use specialized types from a general interface. I thought about the following signatures:
interface IGreatInterface {
Object aMethodAlpha<U>(U parameter) where U : IAnInterface;
Object aMethodBeta(IAnInterface parameter)
}
public class AnInterestingClass : IAnInterface{}
When I try to implement IGreatInterface the compiler flags an error for aMethodBeta() because I've made my T4 to write that method using a subtype of IAnInterface (i.e. I want to implement that method like this: Object aMethodBeta(AnInterestingClass parameter)).
Method aMethodAlpha<U>() can be used but is not as clean as I want because my T4 has to generate some extra code. I (perhaps wrongly)
propose that an implementation of that method, which has to be done by a T4, could be
Object aMethodAlpha<AnInterestingClass>(AnInterestingClass parameter).
I'm thinking that generic methods do not support contravariant types but I'm not sure; I suppose that It's the way the compiler prevents the coder to use a specific type having a method not defined in the general type...
Does a generic method have to use the exact type when being implemented?
Is there any trick to change this behavior?
This question is quite confusing. Let me see if I can clarify it.
When I try to implement IGreatInterface the compiler flags an error for aMethodBeta() because I've made that method using a subtype of IAnInterface I want to implement that method like this: Object aMethodBeta(AnInterestingClass parameter).
That's not legal. Simplifying somewhat:
class Food {}
class Fruit : Food {}
class Meat : Food {}
interface IEater
{
void Eat(Food food);
}
class Vegetarian : IEater
{
public void Eat(Fruit fruit);
}
Class Vegetarian does not fulfill the contract of IEater. You should be able to pass any Food to Eat, but a Vegetarian only accepts Fruit. C# does not support virtual method formal parameter covariance because that is not typesafe.
Now, you might then say, how about this:
interface IFruitEater
{
void Eat(Fruit fruit);
}
class Omnivore : IFruitEater
{
public void Eat(Food food);
}
Now we have got type safety; Omnivore can be used as an IFruitEater because an Omnivore can eat fruit, as well as any other food.
Unfortunately, C# does not support virtual method formal parameter type contravariance even though doing so is in theory typesafe. Few languages do support this.
Similarly, C# does not support virtual method return type variance either.
I'm not sure if that actually answered your question or not. Can you clarify the question?
UPDATE:
What about:
interface IEater
{
void Eat<T>(T t) where T : Food;
}
class Vegetarian : IEater
{
// I only want to eat fruit!
public void Eat<Fruit>(Fruit food) { }
}
Nope, that's not legal either. The contract of IEater is that you will provide a method Eat<T> that can take any T that is a Food. You cannot partially implement the contract, any more than you could do this:
interface IAdder
{
int Add(int x, int y);
}
class Adder : IAdder
{
// I only know how to add two!
public int Add(2, int y){ ... }
}
However, you can do this:
interface IEater<T> where T : Food
{
void Eat(T t);
}
class Vegetarian : IEater<Fruit>
{
public void Eat(Fruit fruit) { }
}
That is perfectly legal. However, you cannot do:
interface IEater<T> where T : Food
{
void Eat(T t);
}
class Omnivore : IEater<Fruit>
{
public void Eat(Food food) { }
}
Because again, C# does not support virtual method formal parameter contravariance or covariance.
Note that C# does support parametric polymorphism covariance when doing so is known to be typesafe. For example, this is legal:
IEnumerable<Fruit> fruit = whatever;
IEnumerable<Food> food = fruit;
A sequence of fruit may be used as a sequence of food. Or,
IComparable<Fruit> fruitComparer = whatever;
IComparable<Apples> appleComparer = fruitComparer;
If you have something that can compare any two fruits then it can compare any two apples.
However, this kind of covariance and contravariance is only legal when all of the following are true: (1) the variance is provably typesafe, (2) the author of the type added variance annotations indicating the desired co- and contra-variances, (3) the varying type arguments involved are all reference types, (4) the generic type is either a delegate or an interface.
If you want to inherit from a generic interface, see phoog's answer. If you are talking about trying to implement an interface co-variantly, that leads to my discussion below.
Assume:
internal interface IAnInterface { }
public class SomeSubClass : IAnInterface { }
public class AnotherSubClass : IAnInterface { }
public GreatClass : IGreatInterface { ... }
The problem with trying to implement the interface with a more derived (co-variant) argument is there's no guarante when this is called through an interface that an IAnInterface passed in will be a SomeSubClass instance. This is why it's not allowed directly.
IGreatInterface x = new GreatClass();
x.aMethodBeta(new AnotherSubClass());
IF You could do covariance, this would fail because you would be expecting a SomeSubClass but would get a AnotherSubClass.
What you could do is to do explicit interface implementation:
class GreatInterface : IGreatInterface
{
// explicitly implement aMethodBeta() when called from interface reference
object IGreatInterface.aMethodBeta(IAnInterface parameter)
{
// do whatever you'd do on IAnInterface itself...
var newParam = parameter as SomeSubClass;
if (newParam != null)
{
aMethodBeta(newParam);
}
// otherwise do some other action...
}
// This version is visible from the class reference itself and has the
// sub-class parameter
public object aMethodBeta(SomeSubClass parameter)
{
// do whatever
}
}
Thus, if you did this, your interface supports the generic, the class has a more specific method, but still supports the interface. The main difference is you'd need to handle the case where an unexpected implementation of IAnInterface is passed in.
UPDATE: it sounds like you want something like this:
public interface ISomeInterface
{
void SomeMethod<A>(A someArgument);
}
public class SomeClass : ISomeInterface
{
public void SomeMethod<TA>(TA someArgument) where TA : SomeClass
{
}
}
This is not allowed, when you implement a generic method from an interface, the constraints must match.
Maybe you're looking for this:
interface IGreatInterface<in U> where U : IAnInterface
{
Object aMethodAlpha(U parameter);
}
class SomeClass : IAnInterface { /*...*/ }
class GreatClass : IGreatInterface<SomeClass>
{
public Object aMethodAlpha(SomeClass parameter) {}
}
EDIT:
Yes, you are right: if you define a generic method in an interface, you can't implement that method with a concrete method using a compatible type.
How about using a delegate (since delegates support co- and contravariance):
[example deleted because I got the variance backwards -- it doesn't work.]

Categories