Delegate Covariance Confusion Conundrum! - c#

Why does this not work? Do I not understand delegate covariance correctly?
public delegate void MyDelegate(object obj)
public class MyClass
{
public MyClass()
{
//Error: Expected method with 'void MyDelegate(object)' signature
_delegate = MyMethod;
}
private MyDelegate _delegate;
public void MyMethod(SomeObject obj)
{}
}

Correct - you don't understand covariance correctly - yet :) Your code would work if you had the same types but as return values, like this:
public delegate object MyDelegate()
public class MyClass
{
public MyClass()
{
_delegate = MyMethod;
}
private MyDelegate _delegate;
public SomeObject MyMethod() { return null; }
}
That would demonstrate covariance. Alternatively, you can keep it as parameters but switch the types around:
public delegate void MyDelegate(SomeObject obj)
public class MyClass
{
public MyClass()
{
_delegate = MyMethod;
}
private MyDelegate _delegate;
public void MyMethod(object obj) {}
}
This now demonstrates contravariance.
My rule of thumb is to ask myself, "given the delegate, what could I do with it? If I can pass in an argument which would break the method, the conversion should have failed. If the method can return something which would break the caller, the conversion should have failed."
In your code, you could have called:
_delegate(new object());
At that point, poor MyMethod has a parameter which is meant to be of type SomeObject, but is actually of type object. This would be a Very Bad Thing, so the compiler stops it from happening.
Does that all make more sense?

Arguments are contravariant, return types are covariant. If the delegate were to be called with an object that is not an instance of SomeObject, you'd have a typing error. On the other hand, returning SomeObject from a routine wrapped in a delegate that returns object is fine.

You need to use a generic.
EDIT: Why? Because as another poster
noted, Object and SomeObject do not
equate to the same thing as Object may
not be SomeObject. This is the whole
point of Generics in the language.
public delegate void MyDelegate<T>(T obj)
public class MyClass
{
public MyClass()
{
_delegate = MyMethod;
}
private MyDelegate<SomeObject> _delegate;
public void MyMethod(SomeObject obj)
{
}
}

The MyDelegate type declares that you can pass any kind of object in. However, MyMethod only takes objects of type SomeObject. What happens if I try to invoke the delegate passing a different kind of object: _delegate("a string object")? According to the declaration of MyDelegate, this should be allowed, but your function MyMethod can't actually receive a string argument.

From the MSDN link you provided
Covariance permits a method to have a
more derived return type than what is
defined in the delegate.
Contravariance permits a method with
parameter types that are less derived
than in the delegate type.
You're attempting to use a more derived parameter type which isn't supported (although .NET 4.0 probably will since this has sorted out many covariance/contravariance issues).

Covariance and Contravariance is about understanding the Is-a-Principle of inheritance.
In both, covariance and contravariance, s.th. is "passed along", either as return value or as an argument to the delegate method. That which is "passed along" has to be "caught" in a receptacle. In C# – or programming jargon as such – we use the word bucket for what I called receptacle. Sometimes you have to fall back to other words in order to catch the meaning of commonly used jargon words.
Anyway, if you understand inheritance, which most likely any reader here will, then the only thing to pay attention to is that the receptacle, i. e. the bucket used for catching has to be of the same type or less derived type than that which is being passed – this being true for both covariance and contravariance.
Inheritance says you can catch a bird in an animal bucket because the bird is an animal. So if a parameter of a method has to catch a bird you could catch it in an animal bucket (a parameter of type animal), which then is contravariance.
And if your method, i.e. your delegate returns a bird, then the "bucket" also can be a of type bird or less derived (of a parent type) meaning the variable where you catch the return value of the method has to be of the same or less derived type than the return value.
Just switch your thinking to discriminate between that which is being passed and that which catches as then all complexity about covariance and contravariance dissolves nicely. Then you realize that the same principle is at work. It is just that inheritance cannot be violated as it flows only one way.
And the compiler is so smart that when you cast the bucket in the more specialized type (again, and as need be) that then and only then you get all the specialized methods back that were added into the more derived class. That is the beauty of it. So it is catch, cast and use what you have and perhaps need.

Related

why covariant type parameters are used only for the return types of the members?

Why covariant type paramaters like IEnumerable<out T> type T used only for the return type (read-only) or inverse contravariant type parameter like Action<in T> type T used only as parameter type (write-only)?
in other word i think there exist a relation between pure covariant concept and c# covariant type paramaters used only for the return types of the members.
Why do covariant type parameters like IEnumerable<out T> have type T used only for the return type?
First off: T does not have to be used only for the return type. For example:
interface I1<out T>{ T M1(); }
interface I2<out T>{ void M2(Action<I1<T>> a); }
In I1<T>, T is used only in return type positions. But in I2, T is used in an input a, and an I1<T> is the input of the Action, so in a sense it is being used in two input positions here.
But let's consider just a simpler case. Why can we make I1 covariant in T but not contravariant in T?
The reason is because covariance is safe and contravariance is not. We can see that covariance is safe:
class Animal {}
class Mammal : Animal {}
class Tiger : Mammal {}
class Giraffe : Mammal {}
class C : I1<Mammal> {
public Mammal M1() { return new Tiger(); }
}
I1<Mammal> i1m = new C(); // Legal
I1<Animal> i1a = i1m; // Legal
Animal a = i1a.M1(); // Returns a tiger; assigned to animal, good!
No matter what C.M1 returns, it is always a Mammal and therefore always an Animal.
But this cannot be legal:
I1<Giraffe> i1g = i1m; // Not legal
Giraffe g = i1g.M1(); // Returns a tiger; assigned to giraffe, bad!
The first line has to be illegal so that the second line never executes.
Now you should have enough information to figure out why contravariance works the way it does. Remember, you can always come back to a simple example and ask yourself "if this was legal, what mistakes could I make later?" The type system is protecting you from making those mistakes!
Exercise: Do this same analysis of I2<T>. Do you see why it is legal to use T in two input positions even though it is out. (Hint: Action is contravariant, so it reverses the direction of assignment compatibility. What happens if you reverse directions twice?)
So i see where is your problem. Answer should be this way. Let me again use example from MSDN:
static object GetObject() { return null; }
static void SetObject(object obj) { }
static string GetString() { return ""; }
static void SetString(string str) { }
static void Test()
{
// Covariance. A delegate specifies a return type as object,
// but you can assign a method that returns a string.
Func<object> del = GetString;
// Contravariance. A delegate specifies a parameter type as string,
// but you can assign a method that takes an object.
Action<string> del2 = SetObject;
//However you can't use del2 this way, after this assingment:
//del2(new object);
}
This is hard to understand and for me it is quiet high level of abstraction.
Covariance
Let's take a closer look to Func<object> del = GetString;
You are allowed to do such thing because string derives from object, so as long as you will get a method with return type derives from object you don't have problem with it. Imagine you declare the same del so all you know you will get object so you declare a variable:
object returnedType = del2();
You don't have to care about whether del2 return int or string because they derives from object it will be similar with:
object returnedType = "string"; //Here we know what is on the left side
//If we assign to del2 method with return type string.
Contravariance
Now let's take a look to Action<string> del2 = SetObject;
Now you assume that you will get a string to method so if somebody, someday will use your delegate with method like SetObject(object obj) so it will be the same as earlier:
object obj= "string"; //Here we know what is on the right
Assuming
it's all about pure polymorphism. In covariance, we except one general type but it changes nothing for us if we get more specific type.
In contravariance we know what we will passing, but it doesn't matter if we will assign string to string or string to object. (but we can't assign object to string).

Type safety in CoVariance and ContraVariance

I am reading C# in Depth from Jon Skeet. Although I have understood the concept of CoVariance and ContraVariance, but I am unable to understand this line:
Well, covariance is safe when SomeType only describes operations that
return the type parameter—and contravariance is safe when SomeType
only describes operations that accept the type parameter.
Can someone please explain with an example for both, why both are type safe in one direction and not in the other direction?
Updated Question:
I still didn't understand from the answers given. I will try to explain my concern using the same example from the book - C# In Depth.
It explains using the following class hierarchy:
COVARIANCE is: Trying to convert from IEnumerable<Circle> to IEnumerable<IShape>, but it is mentioned that this conversion is type safe only when we are performing while returning it from some method, and not type safe when we are passing it as an IN parameter.
IEnumerable<IShape> GetShapes()
{
IEnumerable<Circle> circles = GetEnumerableOfCircles();
return circles; // Conversion from IEnumerable<Circle> to IEnumerable<IShape> - COVARIANCE
}
void SomeMethod()
{
IEnumerable<Circle> circles = GetEnumerableOfCircles();
DoSomethingWithShapes(circles); // Conversion from IEnumerable<Circle> to IEnumerable<IShape> - COVARIANCE
}
void DoSomethingWithShapes(IEnumerable<IShape> shapes) // Why this COVARIANCE is type unsafe??
{
// do something with Shapes
}
CONTRA VARIANCE is: Trying to convert from IEnumerable<IShape> to IEnumerable<Circle>, which is mentioned to be type safe only while performing it when sending it as an IN parameter.
IEnumerable<Circle> GetShapes()
{
IEnumerable<IShape> shapes = GetEnumerableOfIShapes();
return shapes; // Conversion from IEnumerable<IShape> to IEnumerable<Circle> - Contra-Variance
// Why this Contra-Variance is type unsafe??
}
void SomeMethod()
{
IEnumerable<IShape> shapes = GetEnumerableOfIShapes();
DoSomethingWithCircles(shapes); // Conversion from IEnumerable<IShape> to IEnumerable<Circle> - Contra-Variance
}
void DoSomethingWithCircles(IEnumerable<Circle> circles)
{
// do something with Circles
}
Covariance
Covariance is safe when SomeType only describes operations that return the type parameter
The IEnumerable<out T> interface is probably the most common example of covariance. It is safe because it only returns values of type T (well, specifically an IEnumerator<out T> but does not accept any T objects as parameters.
public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}
This works because IEnumerator<T> is also covariant and only returns T:
public interface IEnumerator<out T> : IDisposable, IEnumerator
{
T Current { get; }
}
If you have a base class called Base and a derived class called Derived, then you can do stuff like this:
IEnumerable<Derived> derivedItems = Something();
IEnumerable<Base> baseItems = derivedItems;
This works because each item in derivedItems is also an instance of Base, so it is perfectly acceptable to assign it the way we just did. However, we cannot assign the other way:
IEnumerable<Base> baseItems = Something();
IEnumerable<Derived> derivedItems = baseItems; // No good!
This isn't safe because there is no guarantee that each instance of Base is also an instance of Derived.
Contravariance
Contravariance is safe when SomeType only describes operations that accept the type parameter
The Action<in T> delegate is a good example of contravariance.
public delegate void Action<in T>(T obj);
It is safe because it only accepts T as a parameter, but doesn't return T.
Contravariance lets you do stuff like this:
Action<Base> baseAction = b => b.DoSomething()
Action<Derived> derivedAction = baseAction;
Derived d = new Derived();
// These 2 lines do the same thing:
baseAction(d);
derivedAction(d);
This works because it is perfectly acceptable to pass an instance of Derived to baseAction. However, it doesn't work the other way around:
Action<Derived> derivedAction = d => d.DoSomething()
Action<Base> baseAction = derivedAction; // No good!
Base b = new Base();
baseAction(b); // This is OK.
derivedAction(b); // This does not work because b may not be an instance of Derived!
This isn't safe because there is no guarantee that an instance of Base will also be an instance of Derived.
Imagine you make an ILogger<in T> interface that knows how to log the details of a T. And let's say you have a Request class and an ExpeditedRequest subclass. Surely an ILogger<Request> should be convertible to an ILogger<ExpeditedRequest>. After all, it can log any request.
Interface ILogger<in T> where T: Request {
void Log(T arg);
}
Now imagine another interface IRequestProducer<out T> that gets the next request in some queue. There are different sources of requests in your system, and of course, some of them can still have different subclasses. In this case, we can't rely on converting an IRequestProducer<Request> to an IRequestProducer<ExpeditedRequest> since it could produce a non-expedited request. But the reverse conversion would work.
Interface IRequestProducer<T> where T: Request {
T GetNextRequest();
}
I understood after reading from MSDN, the following two pages:
https://msdn.microsoft.com/en-us/library/dd469484.aspx
https://msdn.microsoft.com/en-us/library/dd469487.aspx
Actually, I think it will become clear from the book also when I will reach the C# 4 part of the book that will explain in and out keywords with Type Parameters of Generics. Right now, I was reading the Limitations of Generics in C# in the C# 1 part of book.
The statement that I wanted to understand was this:
Well, covariance is safe when SomeType only describes operations that
return the type parameter—and contravariance is safe when SomeType
only describes operations that accept the type parameter.
Besides being safe, it is also not possible to write the interface method in other direction as compiler will complain, as written in the above two pages on msdn:
A type can be declared contravariant in a generic interface or delegate if it is used only as a type of method arguments and not used as a method return type.
In a generic interface, a type parameter can be declared covariant if it satisfies the following conditions:
The type parameter is used only as a return type of interface methods and not used as a type of method arguments.
There are now two points, that made me confuse about the statement:
First , I was misunderstanding the statement itself - I was thinking that Covairance is safe only when an instance of Inteface<T> itself is returned from some method rather than passed as an input to some method. However, it is regarding the type parameter T and interface method. Same for ContraVariance.
Second, Now When I have understood what this statement means - that it is regarding passing/returning the generic type parameter T to/from interface methods. I wanted to know why it is Type Safe only while returning T from interface method in Covariance and why it is Type Safe only while passing T as input to interface method in ContraVariance.
Why it is Type Safe only while returning T in CoVariance:
interface IBase<out T>
{
T Return_T(); // Valid and Type Safe
void Accept_T(T input) // Invalid and Type UnSafe
}
class Sample<T> : IBase<T> { }
class BaseClass {}
class DerivedClass : BaseClass
{}
IBase<BaseClass> ibase = new Sample<BaseClass>();
IBase<DerivedClass> iderived = new Sample<DerivedClass>();
ibase = iderived; // Can be assinged because `T` is Covariant
BaseClass b = new BaseClass();
DerivedClass d = new DerivedClass();
ibase.Return_T(); // At runtime, this will return `DerivedClass` which can be assinged to variable of both base and derived class and is type safe
ibase.Accept_T(b); // The compiler will accept this statement, because at compile time, it accepts an instance of `BaseClass`, but at runtime, it actually needs an instance of `DerivedClass`. So, we are eventually assigning an instance of `BaseClass` to `DerivedClass` which is type unsafe.
Why it is Type Safe only while passing T as an input parameter in ContraVariance:
interface IBase<in T>
{
T Return_T(); // Invalid and Type UnSafe
void Accept_T(T input) // Valid and Type Safe
}
class Sample<T> : IBase<T> { }
class BaseClass {}
class DerivedClass : BaseClass
{}
IBase<BaseClass> ibase = new Sample<BaseClass>();
IBase<DerivedClass> iderived = new Sample<DerivedClass>();
iderived = ibase; // Can be assinged because `T` is Contravariant
BaseClass b = new BaseClass();
DerivedClass d = new DerivedClass();
iderived.Accept_T(d); // This is Type Safe, because both at compile time and runtime, either instance of `DerivedClass` can be assinged to `BaseClass` or instance of `BaseClass` can be assinged to `BaseClass`
DerivedClass d2 = iderived.Return_T(); // This is type unsafe, because this statement is valid at compile time, but at runtime, this will return an instance of `BaseClass` which is getting assinged to `DerivedClass`

I can only cast a contravariant delegate with "as"

I'm trying to cast a contravariant delegate but for some reason I can only do it using the "as" operator.
interface MyInterface { }
delegate void MyFuncType<in InType>(InType input);
class MyClass<T> where T : MyInterface
{
public void callDelegate(MyFuncType<MyInterface> func)
{
MyFuncType<T> castFunc1 = (MyFuncType <T>) func; //Error
MyFuncType<T> castFunc2 = func as MyFuncType<T>;
MyFuncType<T> castFunc3 = func is MyFuncType<T> ? (MyFuncType<T>)func : (MyFuncType<T>)null; //Error
}
}
castFunc2 works fine but castFunc1 and castFunc3 cause the error:
Cannot convert type 'delegateCovariance.MyFuncType<myNamespace.MyInterface>' to myNamespace.MyFuncType<T>'
The MSDN article on the as operator states that castFunc2 and castFunc3 are "equivalent" so I don't understand how only one of them could cause an error. Another piece of this that is confusing me is that changing MyInterface from an interface to a class gets rid of the error.
Can anyone help me understand what is going on here?
Thanks!
Add a constraint such that T must be a class.
class MyClass<T> where T: class, MyInterface
This gives the compiler enough information to know that T is convertible. You don't need the explicit cast either.
Variance only applies to reference types. T is allowed to be a value type without the constraint which breaks the compilers ability to prove that T is compatible for contravariance.
The reason the second statement works is because as actually can perform a null conversion. For example:
class SomeClass { }
interface SomeInterface { }
static void Main(string[] args)
{
SomeClass foo = null;
SomeInterface bar = foo as SomeInterface;
}
Foo is obviously not directly convertable to SomeInterface, but it still succeeds because a null conversion can still take place. Your MSDN reference may be correct for most scenarios, but the generated IL code is very different which means they are fundamentally different from a technical perspective.
Eric Lippert gave a great explanation of this issue in his recent posts: An "is" operator puzzle, part one, An "is" operator puzzle, part two.
Main rationale behind this behavior is following: "is" (or "as") operators are not the same as a cast.
"as" operator can result non-null result event if corresponding cast would be illegal, and this is especially true when we're dealing with type arguments.
Basically, cast operator in your case means (as Eric said) that "I know that this value is of the given type, even though the compiler does not know that, the compiler should allow it" or "I know that this value is not of the given type; generate special-purpose, type-specific code to convert a value of one type to a value of a different type."
Later case deals with value conversions like double-to-integer conversion and we can ignore this meaning in current context.
And generic type arguments are not logical in the first context neither. If you're dealing with a generic type argument, than why you're not stating this "contract" clearly by using generic type argument?
I'm not 100% sure what you're want to achieve, but you can omit special type from your method and freely use generic argument instead:
class MyClass<T> where T : MyInterface
{
public void callDelegate(Action<T> func)
{
}
}
class MyClass2
{
public void callDelegate<T>(Action<T> func)
where T : MyInterface
{
}
}
Otherwise you should use as operator with check for null instead of type check.
Your class says that T implements MyInterface because MyInterface is not a instance type. Therefore, MyFuncType<T> is not guaranteed to be MyFuncType<MyInterface>. It could be MyFuncType<SomeType> and SomeType : MyInterface, but that wouldn't be the same as SomeOtherType : MyInterface. Make sense?

Passing Delegate object to method with Func<> parameter

I have a method Foo4 that accepts a parameter of the type Func<>. If I pass a parameter of anonymous type , I get no error. But if I create and pass an object of the type 'delegate' that references to a Method with correct signature, I get compiler error. I am not able to understand why I am getting error in this case.
class Learn6
{
delegate string Mydelegate(int a);
public void Start()
{
Mydelegate objMydelegate = new Mydelegate(Foo1);
//No Error
Foo4(delegate(int s) { return s.ToString(); });
//This line gives compiler error.
Foo4(objMydelegate);
}
public string Foo1(int a) { return a.ToString();}
public void Foo4(Func<int, string> F) { Console.WriteLine(F(42)); }
}
It works if you pass a reference to the method directly:
Foo4(Foo1);
This is because actual delegates with the same shape are not inherently considered compatible. If the contracts are implicit, the compiler infers the contract and matches them up. If they are explicit (e.g. declared types) no inference is performed - they are simply different types.
It is similar to:
public class Foo
{
public string Property {get;set;}
}
public class Bar
{
public string Property {get;set;}
}
We can see the two classes have the same signature and are "compatible", but the compiler sees them as two different types, and nothing more.
Because Func<int, string> and MyDelegate are different declared types. They happen to be compatible with the same set of methods; but there is no implicit conversion between them.
//This line gives compiler error.
Foo4(objMydelegate);
//This works ok.
Foo4(objMydelegate.Invoke);
depends on the scenario, but in the general case there's no reason to keep around the Mydelegate type, just use Func<int, string> everywhere :)

What are the advantages of using generics in method signatures?

(Thanks everyone for the answers, here is my refactored example, in turn another StackOverflow question about the Single Responsibility Principle.)
Coming from PHP to C#, this syntax was intimidating:
container.RegisterType<Customer>("customer1");
until I realized it expresses the same thing as:
container.RegisterType(typeof(Customer), "customer1");
as I demonstrate in the code below.
So is there some reason why generics is used here (e.g. throughout Unity and most C# IoC containers) other than it just being a cleaner syntax, i.e. you don't need the typeof() when sending the type?
using System;
namespace TestGenericParameter
{
class Program
{
static void Main(string[] args)
{
Container container = new Container();
container.RegisterType<Customer>("test");
container.RegisterType(typeof(Customer), "test");
Console.ReadLine();
}
}
public class Container
{
public void RegisterType<T>(string dummy)
{
Console.WriteLine("Type={0}, dummy={1}, name of class={2}", typeof(T), dummy, typeof(T).Name);
}
public void RegisterType(Type T, string dummy)
{
Console.WriteLine("Type={0}, dummy={1}, name of class={2}", T, dummy, T.Name);
}
}
public class Customer {}
}
//OUTPUT:
//Type=TestGenericParameter.Customer, dummy=test, name of class=Customer
//Type=TestGenericParameter.Customer, dummy=test, name of class=Customer
One reason when generics are very useful is when the generic type parameter is used as the type of a parameter or as the return type of the method.
That means, you can write methods like
public T GetAs<T>(string name)
where the return type can be checked by the compiler and boxing value types can sometimes be avoided.
The caller would write:
int value = GetAs<int>("foo");
Whithout generics, you would have to write
public object GetAs(Type t, string name)
and the caller has to cast the result again:
int value = (int)GetAs(typeof(int), "foo");
A primary reason is the type safety at compile time. If you are passing two Type objects you are placing the responsibility at the developer instead of the compiler.
This is also why many IoC containers utilizes it, as your compiler will complain if an concrete type isn't inheriting the abstract type.
public void Register<TAbstract, TConcrete>() where TConcrete : TAbstract
{
}
This code will only work if TConcrete is implementing or inheriting TAbstract. If this method took two Type parameters, your method should validate this relationship.
A simple answer is type inference where possible.
If the generic type is used in the method signature, you can omit it because the type could be inferred:
void SomeMethod<T>(T x, T y) where T : IComparable<T> {
Console.WriteLine("Result: {0} to {1} is {2}", x, y, x.CompareTo(y));
}
So the usage is simplified:
SomeMethod(3, 4); // instead of SomeMethod<int>(3, 4);
SomeMethod("one", "two"); // instead of SomeMethod<string>("one", "two");
If the generic type parameter is not used in the method signature the type inference is not possible:
var emptySequence = Enumerable.Empty<int>();
I think one of the primary uses is type safety with arguments and return values. In your example case, there is not much use for generics, because the input/output types (string) do not match the generic case (customers).
A more appropriate use might be:
public T RegisterType<T>(string name)
{
T obj = new T();
obj.DoSomething();
return obj;
}
or maybe
public void DoSomething<T>(T obj)
{
//operate on obj
}
If you didn't use Generics, you'd either have to overload a method for each type you want to support, or you'd have to accept the parameter as an object and perform casting logic.
For one example, compare the code needed to create an instance of your type using the typeof option versus a generic. Or return an instance of the type. Or accept an instance of the type as an argument. Or set a property on an instance of the type.
In general, if you will be working only with the type itself you can accept a type parameter. If you want to do anything with an instance of the type, use a generic.
Another reason to use a generic is if you want to apply constraints to the type. For example, you can require the type to implement one or several interfaces, inherit another type, be a reference type or value type, have a default constructor, or some combination of the above. The compiler will enforce this so you can't build code that doesn't comply with your requirements.
I'd say the best reason is type safety, using the "where" keyword, to ensure that the generic type is of a certain type (or sub-class/implementor). Using "typeof" will let you send anything through.

Categories

Resources