C# Casting generics (covariance and contravariance?) - c#

I need some advice/help on this, I can't see the wood from the trees any more.
It's a straight forward series of classes implementing some interfaces using generics.
Then I'm trying to cast the concrete types for example:
MyGenericObject<SomeObject> _obj;
IMyGenericObject<ISomeObject> _genObj = (IMyGenericObject<ISomeObject>)_obj;
// Invalid cast
I've read some articles about covariance and contravariance but not too clear why this wouldn't be possible, or how to get round it?
So, in this example:
public interface IMyObject<in T> where T : IBaseObject
{
T Activity { get; set; }
}
wouldn't work...
....because, you can't get and set the Activity property.
In this example, I needed to do:
public interface IMyObject<out T> where T : IBaseObject
{
T Activity { get; }
}
hope that helps someone, and thanks to all for help!

You can only do that if you declare the interface as having a covariant (out) parameter. You can only do that if the parameter is used covariantly.
For example, if the interface IMyGenericObject<T> has a method taking a T parameter, this prevents you from declaring the parameter as covariant. Conversely, if there is a method that returns a T, that prevents you from declaring the parameter as contravariant.
EDIT
In response to your comment on SLaks's answer, I'm tempted to repeat everything Eric Lippert has ever written on co- and contravariance. See http://blogs.msdn.com/b/ericlippert/archive/tags/Covariance+and+Contravariance/ and also his answers in SO (most recently https://stackoverflow.com/a/8380213/385844)
To summarize:
You can't cast IList<string> to IList<object> because it's legal to pass a FileInfo to an IList<object>, but it is not legal to pass it to an IList<string>.
You can't cast an IList<object> to an IList<string>, because it's legal to retrieve an item from an IList<string> and assign it to a string reference, but an IList<object> might contain a FileInfo, which can't be assigned to a string reference.
EDIT 2
Since you asked for advice, it's also possible to split your interfaces into co- and contravariant parts. To continue with the list example, you could have these interfaces
public interface ICovariantList<out T>
{
T this[int index] { get; }
//...
}
public interface IContravariantList<in T>
{
T this[int index] { set; }
void Add(T item);
//...
}
public class SomeList<T> : ICovariantList<T>, IContravariantList<T>
{
//...
}
This allows you to use the class covariantly or contravariantly, depending on the context.

You need to declare the interface as having a covariant (out) generic parameter.

Related

C# covariance issue with two covariant interfaces [duplicate]

This question already has an answer here:
Parameter must be input-safe error
(1 answer)
Closed 4 years ago.
I have two interfaces that are both covariant, with both being passed in to each other like so:
public interface Perfomer<in T>
{
void Perform(T t, Tracer<T> tracer);
}
public interface Tracer<in T>
{
void Notify();
}
However even though both interfaces are marked covariant, and T is only ever being used as input, I'm still getting the error:
"Invalid variance: The type parameter 'T' must be covariantly valid on
'Perfomer<T>.Do(T, Tracer<T>)'. 'T' is contravariant. [_Console].
Any ideas why having covariant interface parameter using the same type makes T contravariant?
Edit
(Sorry, I am new to StackOverflow, based on the answers I realize I should've been more exact in my question, I had just tried to eliminate as much noise as possible to a single error).
The code actually has two interfaces with generally similar interfaces:
public interface Performer<in T>
{
bool Perform(T t, Tracer<T> tracer = null);
}
public interface Tracer<in T>
{
void Notify(Performer<T> performer, T t, ref bool success);
}
It's purpose is to allow the an optional "tracer" to see things happen/modify the results of a performer.
When you declare that Performer is contravariant, you are declaring that anything a Performer does to a T can also be done to a more specific version of T. For example, an action that acts on a object can be given a string, and it'll just act as if that string is an object.
So for example you could do this, because all streams support Length:
class MyClass : Performer<Stream>
{
void Perform(Stream t)
{
Console.WriteLine(t.Length)
}
}
Performer<FileStream> p = new MyClass();
p.Perform(new FileStream());
But you can't do this, because you gave it a class that doesn't support IsAsync:
class MyClass : Performer<FileStream>
{
void Perform(Stream t)
{
Console.WriteLine(t.IsAsync)
}
}
Performer<Stream> p = new MyClass();
p.Perform(new Stream()); //Stream isn't good enough; it has to be a FileStream, since it needs IsAsync
So far so good. Now let's add in that second parameter:
class MyClass : Performer<Stream>
{
void Perform(Stream t, Tracer<Stream> tracer)
{
Console.WriteLine(tracer.Notify())
}
}
In order for this to work, the contravariance has to work. If the contravariance works, it means that Perform can store a Tracer<FileStream> (which you pass in) in a variable that is typed as a Tracer<Stream> (which is how it is implemented). That means that Tracer must be covariant with respect to its type argument.
So you can fix your code by changing in to out, like so:
public interface Performer<in T>
{
void Perform(T t, Tracer<T> tracer);
}
public interface Tracer<out T> //out instead of in
{
void Notify();
}
From what you've provided I'd avoid the issue all together, Modify the Tracer interface to remove the T because it's not needed:
public interface INotify
{
void Notify();
}
Then just take in an the new interface in your performer
public interface Perfomer<in T>
{
void Perform(T t, INotify entity);
}
PS: there might be a type in your interface name Perfomer => Performer
Just modifyTracer<in T> to Tracer (non-generic) and define void Perform(T t, Tracer tracer);.
Your code was not using T in Tracer anyways.
Since you edited your question with new details, the alternative fix is to remove in from generics definition. You don't need it. Another way to achieve what you want is following:
public interface Performer<T>
{
bool Perform(T t, Tracer tracer = null);
}
public interface Tracer
{
bool Notify<T>(Performer<T> performer);
}
Note: drop ref bool and return bool instead

Cannot use both covariance and contravariance on interface

I’m having some problems with interfaces and co/contravariance and I’m having some problems. Imagine a structure like below (excuse any obvious mistakes, I’m in mobile at the moment)
public interface IDelimitedFileReader<T>
{
IEnumerable<T> Read(string file);
}
public interface IMapper<T> where T : IManifestItem
{
MappedRecord Map(IEnumerable<T> items);
}
public interface IProfile<T> where T : IManifestItem
{
IDelimitedFileReader<T> Reader { get; }
IMapper<T> Mapper { get; }
}
public class ProfileImpl : IProfile<ManifestItemImpl>
{
IDelimitedFileReader<ManifestItemImpl> Reader => new DelimitedFileReaderImpl<ManifestItemImpl>();
IMapper<ManifestItemImpl> Mapper => new MapperImpl<ManifestItemImpl>();
}
public static class ProfileRetriever
{
public static IProfile<IManifestItem> GetProfile()
{
return new ProfileImpl();
}
}
However my GetProfile method complains that the return types do not match. I believe this is because the IProfile interface needs to be covariant AND contravariant - if I remove the Mapper property on the interface and implementation, and change T to be ‘in’ in IProfile, it works. If I remove Reader, it works if I make T to ‘out’. I need to do both but obviously can’t!
Am I being really stupid or is what I want impossible? Thanks!
T is contravariant in IMapper but covariant in IProfile and IDelimitedFileReader. The definition of something covariant and contravariant is invariant, which means that T doesn't allow any type variance; similar to IList. Your current set up will not work.
The issue should be made clear if you declare the desired variance in all interfaces (as you should do, otherwise you'll get cryptic error messages because the compiler can't do any better):
interface IDelimitedFileReader<out T> { /*...*/ }
interface IMapper<in T> { /*...*/ }
interface IProfile<out T> { /*...*/ } //at first glance it looks covariant
If you try to compile this, the compiler will give you a precise error of whats wrong.
If you declare T invariant in IProfile: interface IProfile<T> { //... } then the error will go away and the code should compile but your stuck with an invariant interface.

Is it possible to use/extract the type used by a generic to define a second generic's type?

I know the title may be a little unclear, so I'll explain what I'm trying to do.
Note, this is more for educational reasons around the language's capabilities. In other words if this is possible, not should this be the way to go about it.
Consider the following generic class:
public class Foo<TId>
{
TId Id { get; set; }
}
Now consider concrete subclasses based on the above. Here's two examples using 'int' and 'string'...
public class IntFoo : Foo<int>
{
}
public class StrFoo : Foo<string>
{
}
And finally a generic that takes a Foo as a type parameter, and inherits from a Laa which takes its type parameter from Foo.
public class BaseClass<TFoo, TFooId> : Laa<TFooId>
{
}
public class Laa<TFooId>
{
}
Here's how you'd do the one based on an int and string, but note in addition to IntFoo and StrFoo, I have to also define int and foo explicitly...
public class IntFinal : BaseClass<IntFoo, int>
{
char somePropSpecificToIntFinal{ get; set; }
}
public class StrFinal : BaseClass<StrFoo, string>
{
char somePropSpecificToStrFinal{ get; set; }
}
Note that these 'final' classes are concrete types with their own properties which can't be reduced to a generic that takes a type (i.e. using a generic with the single type T, that then subclasses another generic that takes Foo and T as its arguments.
I'm wondering is if there's a way to have that type inferred so it can be written like so...
public class IntFinal : BaseClass<IntFoo>
{
}
public class StrFinal : BaseClass<StrFoo>
{
}
...and have the type for Laa implied from the generic specified on Foo. Here's a pseudo-code example of what I want.
public class BaseClass<TFoo> : Laa<TFoo.IdType>
{
}
So is that possible in C#?
Note, if this can't be done with classes, can it be done with interfaces?
Consider this...
interface IFoo
{
Type FoosType { get; }
}
public class Foo<TId> : Foo
{
TId Id { get; set; }
Type FoosType { get{ return TId } }
}
Then do this...
public class BaseClass<TFoo> : Laa<TFoo.FoosType>
where TFoo : Foo
{
}
(Note: FoosType would have to be static technically, and you can't inherit using statics so again, this is pseudo-code.)
If you constrained TFoo to IFoo, could you then use 'FoosType' as the type specifier when defining Laa?
You can't do that based on the C# specification. Type inference currently works for methods only and doesn't work for types (classes like your case)
Second rule that breaks your needed result is that you can't specify one generic type argument and infer the other, it is Provide all or Infer all case for methods.
C# specification:
1.6.3 Type parameters
When the generic class is used, type arguments must be provided for each of the type parameters
Your question is not very specific, and it's not clear what the actual constraints and requirements are. That said…
Type inference only occurs for generic methods, not generic types. So taking your question literally, the answer is no, there is no way to infer the type.
What might work for you is to use Foo<TId> in the class definition instead of IntFoo:
class BaseClass<TFooId> : Laa<TFooId>
{
public Foo<TFooId> Method() { ... }
}
(Of course, you can apply the type anywhere appropriate: fields, property types, etc.)
I.e. instead of coding the BaseClass type with two type parameters, just use the one that uniquely defines the interesting/useful elements of the Foo<TFooId> base class you're using, and then use that base type instead of the more-derived IntFoo
In your example, you have no constraints for the TFoo class, so it's not like BaseClass<TFoo, TFooId> was going to be able to use even the base type class members from Foo<TId> anyway. But even if you did mean to constrain TFoo to Foo<TFooId>, it seems likely you wouldn't really need to specify that type anyway.
If the above is not useful, then you need to add a lot more detail to your question, to explain precisely what is needed. Consider also that it's likely people have already gone down this road, and that if you express your question less about the mechanics of the implementation you think you need, you instead phrase it at a higher level, you might likely find existing questions on Stack Overflow or articles elsewhere that already address that broader question.
At the very least, if you are unable to find such references yourself, expressing your question that way may yield better answers faster.
See also XY Problem.

C# refuses to accept generic subclass type

I keep seeing this error, over and over again:
Cannot convert from Item<Foo> to Item<IFoo>.
This is clearly nonsense; an object of type Item<Foo> is statically guaranteed to be able to do absolutely everything that an Item<IFoo> can do (and possibly more), so why is the compiler refusing to accept my perfectly valid code?
I have a method that accepts an Item<IFoo> as an argument. For some reason, it refuses to accept an Item<Foo> as input, even though Foo implements IFoo. This makes no sense at all. I can pass a Foo in place of an IFoo, but I can't pass an Item<Foo> in place of an Item<IFoo>. Why?
public class Item<T>
{
public readonly int ID;
public readonly T Data;
...
}
public void ProcessItem(Item<IFoo> item)
{
Console.WriteLine(item.ID);
}
ProcessItem(new Item<Foo>());
Classes in C# are invariant, so depending on your requirements you'll have to create an interface and implment that:
public interface IItem<out T> { ... }
public class Item<T> : IItem<T> { ... }
IItem<IFoo> item = new Item<Foo>();
Note that it is not necessarily safe to assign a Class<Subtype> to a Class<Basetype>. A common example is List<T>:
List<object> l = new List<string>(); //won't compile
l.Add(3);
C# only allows variance annotations on interfaces and delegates, and only when it is safe to do so.

Adding constraints to an interface property

I'm writing an interface and I want to declare a property that returns a generic collection. The elements of the collection should implement an interface. Is this possible and, if so, what is the syntax.
This doesn't compile, what's the right way to do this?
interface IHouse
{
IEnumerable<T> Bedrooms { get; } where T : IRoom
}
Thanks
Why use generics? Just do:
interface IHouse
{
IEnumerable<IRoom> Bedrooms { get; }
}
This is cleaner, and since you're already restricting to the interface, it will act nearly identically.
You have to mark the interface as generic as well:
interface IHouse<T> where T : IRoom
{
IEnumerable<T> Bedrooms { get; }
}
There are no good reasons why Microsoft chose to add this restriction. Under the hood properties are just a pair of methods, and generic methods are already supported.
I have hit this restriction a couple of times, and had to resort to adding explicit get and set methods instead:
interface IHouse
{
IEnumerable<T> GetBedrooms<T>() where T : IRoom;
}
Generic is for classes and methods, not properties. If a class/interface is generic, then you can use the type in the property. I agree with Reed's solution.

Categories

Resources