简体   繁体   English

代表中的协方差,任何例子?

[英]Covariance in delegate, any example?

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. 我正在阅读这篇msdn文章 ,逆向示例(键盘和鼠标事件)非常有用,而协方差示例(Mammal和Dog)看起来并不那么好。

The keyboard and mouse event is great since you can use 1 handler for multiple cases; 键盘和鼠标事件非常棒,因为您可以为多个案例使用1个处理程序; 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: 现在,我正在做container.Register<ISomeService>(() => new SomeService())Func委托的协方差在两个层面上发挥作用:

  1. 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 我可以传入一个Func<SomeService> ,它可以在Func<ISomeService>没有问题的地方分配,因为SomeService是一个ISomeService
  2. 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 Register方法中,可以在Func<object>位置分配Func<T> ,没有任何问题,因为任何引用类型都是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. 如果Func<SomeService>无法分配给Func<ISomeService> ,或者Func<ISomeService>无法分配给Func<object> (通过协方差),则此示例将不起作用。

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). 你是对的,用一个值返回一个事件并不常见,这是一种约定(实际上它不仅仅是约定:看额外阅读#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). 然而,代表不仅仅是为了事件,基本上它们是近半个世纪的旧C风格“指向函数的指针”(C术语)的.NET版本。

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. 在控制流概念中留下事件和委托人和听众(java)的神秘面孔 ,它们只是简单的回调 ,因此如果它们具有返回值则完全合理。

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. 我想使用函数指针(我的意思是:委托或lambda)来完成这个处理的一部分 Lets call it FeedAnimal. 让我们称之为FeedAnimal。 I would like to have an other skeleton method which calls this feed method, lets call it CareAnimal. 我想有一个其他骨架方法调用这个feed方法,让我们称之为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. 我想使用运行时变量模式将feed算法插入到care算法中,因此Care将有一个委托参数: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. 现在重点:Feed将有不同的Dog和Cat实现,一个返回Dog,另一个返回Cat ....而Care()方法接受一个返回Animal的delegate参数。

[Extra reading #1]: This kind of polymorphic implementation is the not OOP polymorphism implementaion. [阅读外#1]:这种多态实现的是不是 OOP多态性implementaion。 In OOP you can achieve similar with virtual method overloads. 在OOP中,您可以实现与虚拟方法重载类似的操作。

[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. [额外阅读#2]:关于委托(和事件)它们是多播委托的真正令人不安的事情我的意思是单个委托(默认情况下是多播委托)可以包含许多方法入口点。 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. 当然这很令人困惑,所以我们安全地说:如果我们使用委托(或事件)的多播功能,那么除了void返回之外没有任何意义。 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. 事件通常是多播的,这来自发布者/订阅者DP的比喻:许多订阅者(处理者)可以在知道彼此的任何事情的情况下订阅(+ =)到发布者出版物。

Well, if you look at the declaration of the Func<T, TResult> Delegate. 好吧,如果你看一下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 . 熟悉的Linq扩展Select ,有一个接受此委托的重载

Additionally, note that the return type of Select is IEnumerable<T> , that is a covariant interface, ie 另外,请注意Select的返回类型是IEnumerable<T> ,即协变接口,即

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. 我可以将此委托传递给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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM