I'm reading this msdn article, the contravariance example (keyboard and mouse event) is great and useful, while the covariance example (Mammal and Dog) doesn't look so.
The keyboard and mouse event is great since you can use 1 handler for multiple cases; but I can't think of the advantages of assigning a handler that returns a more derived type to a handler returning base type, not to mention it looks like less common to have a delegate which cares about return type?
Could someone please provide a more practical example of covariance in delegate?
Here's a "real world" example where I implement a service locator. I want to make a class where I can register "factory" delegates that know how to produce instances of some type, then resolve instances of some type later on. Here is what that looks like:
interface ISomeService { ... }
class SomeService : ISomeService { ... }
class IocContainer
{
private readonly Dictionary<Type, Func<object>> _factories = new Dictionary<Type, Func<object>>();
public void Register<T>(Func<T> factory)
where T: class
{
_factories[typeof(T)] = factory;
}
// Note: this is C#6, refactor if not using VS2015
public T Resolve<T>() => (T)_factories[typeof(T)]();
}
class Program
{
static void Main(string[] args)
{
var container = new IocContainer();
container.Register<ISomeService>(() => new SomeService());
// ...
var service = container.Resolve<ISomeService>();
}
}
Now, where I'm doing container.Register<ISomeService>(() => new SomeService()), the covariance of Func delegates comes into play on two levels:
I can pass in a Func<SomeService> and it is assignable where a Func<ISomeService> is expected with no problems, because a SomeService is a ISomeService
Inside the Register method, a Func<T> can be assigned where a Func<object> is expected with no problems, because any reference type is an object
If a Func<SomeService> wasn't assignable to a Func<ISomeService>, or Func<ISomeService> wasn't assignable to a Func<object> (via covariance), this example wouldn't work.
You are right, it is not common an event to return with a value, it's kinda convention (actually it is more than convention: see extra reading #2). However delegates are not only for events, basically they are the .NET version of the near half century old C style "pointer to a function" (C terminology).
To leave the mystery left on events and delegates and listeners (java) in control flow concept they are just simple callbacks, so it is completely reasonable if they have return value.
So from this callback point of view:
Say I would like to process animals. I would like to use a function pointer (I mean: delegate or lambda) to do a part of this processing. Lets call it FeedAnimal. I would like to have an other skeleton method which calls this feed method, lets call it CareAnimal. I would like to plugin the feed algorithm to the care algorithm with run time variable mode, so the Care will have a delegate parameter: feed. After feeding the feed method returns an animal.
Now the point: The feed will have different implementations for Dog and Cat, one returns with Dog and the other returns with Cat.... and the Care() method accepts a delegate parameter which returns with Animal.
[Extra reading #1]: This kind of polymorphic implementation is the not OOP polymorphism implementaion. In OOP you can achieve similar with virtual method overloads.
[Extra reading #2]: The really disturbing thing about delegates (and events) that they are multicast delegates I mean one single delegate (which is a multicast delegate by default) can contain many method entry point. When it is invoked then all contained methods invoked in a cycle in not specified order. However there will be one return value in case the signature is not void. Of course this is confusing, so we safely say: If we use the multicast feature of delegates (or events) then it makes no sense other signature than void return.
Events are typically multicast, this comes from the Publisher/Subscriber DP metaphore: Many subscriber (handler) can subscribe (+=) to the publishers publication without knowing anything about each other.
Well, if you look at the declaration of the Func<T, TResult> Delegate.
public delegate TResult Func<in T, out TResult>(
T arg
)
You can see that the type of the input parameter is contravariant but the type of the result or return value is covariant.
The familiar Linq extension Select, has an overload that accepts this delegate.
Additionally, note that the return type of Select is IEnumerable<T>,
that is a covariant interface, i.e.
public IEnumerable<out T>
{
\\ ...
}
Now consider the types,
abstract class Mammal
{
}
and
class Dog : Mammal
{
}
I can declare an instance of the delegate.
var doItToMammals = new Func<Mammal, Mammal>(mammal => mammal);
I can pass this delegate to Select without variance.
IEnumerable<Mammal> mammals = new List<Mammal>().Select(doItToMammals);
Now, because the input of the function is contravariant, I can do
IEnumerable<Mammal> mammals = new List<Dog>().Select(doItToMammals);
Now here's the point, because the result is covariant, I can do
IEnumerable<Dogs> dogs = new List<Dog>().Select<Dog, Dog>(doItToMammals);
Related
I would like to define a non-generic delegate extending a generic delegate to define a default for the generic delegate type parameter, i.e. something like this (not a valid piece of C# code but an obvious-to-understand illustration for the concept I mean hopefully):
public delegate void MyDelegate<T>(T arg);
public delegate void MyDelegate(object arg) : MyDelegate<object>;
What is, if any, the correct way to declare this?
Needless to say I can just declare 2 independent delegates to achieve virtually same effect a way like this:
public delegate void MyDelegate<T>(T arg);
public delegate void MyDelegate(object arg);
but I'd like to do to make the actual relation between them (the fact a bare MyDelegate type is meant to equal MyDelegate<object>, the way like a class extending another class or literally) definite and make the following code will become valid if possible:
MyDelegate<object> d1;
MyDelegate d2;
MyDelegate d1 = d2;
Sorry, I may have been too harsh in saying you should know delegate types are sealed. It is clear in the specification, but I took a look at the latest version of the MSDN language documentation, and it does not seem to mention this at all. Well, anyway…they are sealed. 😊 You can't create a new class that inherits a delegate type, and you certainly can't use the delegate keyword to declare a new delegate type that inherits another delegate type (the keyword has a very specific syntax, and doesn't even allow an inheritance syntax).
It's still not really clear to me what you're trying to do. But, in some sense, a delegate type is an interface with just one member. It's more flexible, of course, because the implementation isn't in a single type, but can be any method from any type. But conceptually, the two are very similar.
In that sense, you could express your intent in code using an interface instead of a delegate. For example:
interface IDelegate<T>
{
void M(T arg);
}
interface IDelegateObject : IDelegate<object> { }
Of course, the problem with this is that now you need some type that will implement the interface in question. Any given type will only be able to implement any given interface once, so you lose a lot of the flexibility that delegate types would ordinarily give you.
But, you can do something like this:
IDelegate<object> d1;
IDelegateObject d2;
d1 = d2;
That is, since IDelegateObject inherits IDelegate<object>, and thus "is a" IDelegate<object>, you can then assign a value of type IDelegateObject, i.e. d2, to a variable of type IDelegate<object>, i.e. d1.
I recall that the C# compiler, when translating from the query expression syntax to calls to the relevant operator methods on the monad, doesn't really care about and doesn't require the monad to have implemented some interfaces.
It doesn't even care of the operator methods are extensions or real instance methods.
But I can't recall the details at all. It seems like there is a bare minimum requirement for the sequence itself, which, in my query below would be the new Foo<string>() expression.
I recall it doesn't necessarily have to adhere to an interface but it either did have to have a method named GetEnumerator or it did have to implement the IEnumerator<T> methods. But I could be wrong since my example query shown below works with or without the presence of a GetEnumerator method in the Foo<T> class.
However, when I introduce a where query expression, that doesn't resolve the type of the receiver.
The Select expression works just fine.
I used to know this at the back of my hand until 18 months ago, which was the time I implemented a trivial LINQ provider. I have since forgotten much of it.
Could you please list the bare minimum requirements for all this to work?
using System;
namespace CompilerDoesNotCareAboutTypeLINQQuerySyntax
{
class Program
{
static void Main(string[] args)
{
// What are the minimum requirements on Foo<T>()?
// How does it resolve this expression new Foo<string>
// such that it knows that Foo<string> has many Bar<string>
// or whatever foo in the query below might resolve to?
var query = from foo in new Foo<string>()
/* where foo. // when I do foo., only the system.object inherited properties of Foo show up */
select foo;
}
}
public class Foo<T>
{
public string Name { get; set; }
public Foo<T> Where<T>(Func<T, bool> predicate)
{
return new Foo<T>();
}
/*IEnumerator<Foo<T>> GetEnumerator()
{
return new List<Foo<T>>().GetEnumerator();
}*/
}
public static class Extensions
{
public static Foo<R> Select<T, R>(this Foo<T> foo, Func<T, R> transformer)
{
return new Foo<R>();
}
}
}
Update
Here's an update of the Where method declaration after implementing the fix that Servy pointed out.
class Foo<T>
{
public Foo<T> Where(Func<T, bool> predicate)
{
return new Foo<T>();
}
}
That fixes the bug due to which foo in the above query expression query resolved to system.object. However, it now resolves to string, which, by inference, is the generic type parameter type of the Where method.
It earlier resolved to object because I had overloaded the generic type parameter T as Servy pointed out, and now it resolves to T, which is the generic type parameter of the declaring type Foo<T>.
The question still remains:
What is the bare minimum requirement on the sequence expression new Foo<string>()? It looks like there is none? Then what is the correlation between the value of the expression that will sit in the place of the sequence and the type of the range variable foo? If I had to make it such that new Foo<T> returned many foos and therefore the foo in the query from foo in new Foo<T>()... resolved to a single Foo<T>, what would need to be done?
The problem here is that you're overloading T in your implementation of Where.
You define T as the generic argument to the type itself, and you also define T as the generic argument to the Where method. Since the method's argument is "closer" it takes precedence. This means that the T in Func<T, bool> is the method arguement's T, *but not the class's T. The class's T is string, but the method's T cannot be inferred to be anything, which is why you're not seeing string members of the parameter.
In this case, it doesn't seem like you need the method to be generic at all, since the type itself is already supplying you with the generic argument. You'd only need that method to be generic if it was an extension method not on the type itself. Just make the method not generic and your code will work just fine.
I have the following extension methods for my MessageBus:
public static class MessageBusMixins
{
public static IDisposable Subscribe<T>(
this IObservable<T> observable,
MessageBus bus)
where T:class
{
...
}
public static IDisposable Subscribe<T>(
this IObservable<Maybe<T>> observable,
MessageBus bus)
{
...
}
}
which compiles fine. However when I try to use it:
IObservable<Maybe<string>> source = ...;
MessageBus bus = ...;
source.Subscribe(bus);
I get the error that neither of the two candidate methods
are most specific. However I thought that Maybe<T> would
be more specific than T or is that not correct?
EDIT
It gets curiouser because if I call the extension method
explicitly then:
MessageBus.SubscribeTo(source, bus);
Then it works and picks the correct method.
Well, you can fix it by specifying the type argument:
source.Subscribe<string>(bus);
... as that's now only the second method is applicable.
Otherwise, the compiler could call either of:
source.Subscribe<string>(bus);
source.Subscribe<Maybe<string>>(bus);
If you think the first is more specific than the second, you'll have to find the rule in the C# specification which says so :) It's not an unreasonable expectation, but I don't think the normal "more specific" conversions apply to type parameters as well as regular parameters.
So for example, in section 7.5.3.2 of the C# 4 spec ("Better Function Member") there a rule about:
Otherwise if MP has more specific parameter types than MQ, then MP is better than MQ. [... lots of details about less/more specific ...]
... but there's no similar point about type parameters. (The second about normal parameters talks about type arguments, but that's within the parameter types themselves.)
Another alternative is to simply give the methods different names. Do they have subtly different behaviour? If so, why not make that really obvious via the naming? You really don't want someone to get the wrong behaviour just because they were surprised about which overload was called.
I'm a beginner with C# and can't find any answer for this :
Im trying to delegate Actions with some interface parameter , but push through functions with objects that extend this interface (or class)
// some class extending interface
public class IntEvent : IFace{}
public class MoveEvent : IFace{}
// function i like to use to verify Action
static void setAction(Action<IFace> evt) {
// evt ...
}
// function to delegate as Action
static void evtCheck(IntEvent evt) {
// some func
}
static void evtMove(MoveEvent evt) {
// some func
}
// class {
// call method inside class and delegate this function :
setAction(evtCheck);
setAction(evtMove);
I'm receiving an error that "evtCheck(IntEvent) cannot be converted to Action<IFace>" , even if IntEvent extends the IFace interface .
How should I solve this ? Maybe I have to use Func or Delegate ?
You can't do what you're trying to do - you're expecting covariance, but Action<T> is contravariant on its only type-parameter.
You can't do a method-group conversion from evtCheck to Action<IFace> because evtCheck needs a more specific type (IntEvent) as its argument than the more general IFace type that an Action<IFace> instance expects. If such a conversion were allowed, what would you expect to happen if the delegate were executed with an argument that implements IFace but is not an IntEvent?
I think you should have another look at your design because it looks like there's a flaw there, but if you want, you can create a lambda to force a cast of the argument to the desired type and accept the possibility of an InvalidCastException:
setAction(iFace => evtCheck((IntEvent)iface));
More likely, you might want to make evtCheck accept a more general type or setAction to accept a more specific delegate.
Action<T> is contravariant in T, so methods taking an Action<T> will also accept Action<U> where T is derived from U.
Your IntEvent class, however, implements the IFace interface, which means that if the compiler accepted your setAction() call, it would be possible to call evtCheck() with an argument that is another IFace-derivative and not IntEvent, a runtime error and a clear violation of type safety.
Obligatory Eric Lippert reference: Covariance and contravariance
EDIT: from your description it seems to me that what you really want is differentiation based on the method parameter's type, so e.g. you want a separate action for IntEvent, another one for (a hypothetical) DoubleEvent etc. If this is the case, you are actually after double virtual dispatch, which is not directly supported in C#, but you can simulate it using the Visitor design pattern.
The following example is taken from C# in Depth: What you need to master C# 2 and 3, and seems to only only cause a breaking change as jskeet has identified, but be wrong. Please explain:
delegate void SampleDelegate(string x);
public void CandidateAction (string x)
{
Console.WriteLine("Snippet.CandidateAction")
}
public class Derived: Snippet
{
public void CandidateAction (object x)
{
Console.WriteLine("Derived.CandidateAction")
}
}
....
Derived x = new Derived();
SampleDelegate factory = new SampleDelegate (x.CandidateAction);
factory ("test");
Now, why should it work altogether as SampleDelegate accept string not the object. And to my knowledge, object doesn't derive from the string. It is the other way around. That's what contravariance permits under c# 2.0. The seems to demonstrate the opposite effect.
Conceptually, Derived.CandidateAction's signature is saying, "I can handle any object you want to throw at me." SampleDelegate's contract is "You have to be able to handle a string." Now if a method can handle any object, it can certainly handle a string. So Derived.CandidateAction is capable of fulfilling what SampleDelegate needs, and can therefore be assigned to a SampleDelegate variable.
I wrote a more detailed discussion of this (admittedly from a C# 4 point of view) at http://hestia.typepad.com/flatlander/2008/12/c-covariance-and-contravariance-by-example.html.
Contravariance allows you to use a method which has parameters which are a base type of the parameters in the delegate method signature.
The delegate signature defines what types will be passed to the method. The method itself can have types that are less "specific". In your example, when the delegate is invoked as string is being passed. It is perfectly ok for the actual method to only want an object because a variable of type object is allowed to hold an instance of type string.
This is most useful in event handling scenarios where you can write an event handler like this that can be attached to almost any event (even when the args parameter being passed is more specific like ItemCheckedEventArgs):
public void GenericHandler(object source, EventArgs args) {
//do something generic
}