变体接口中的变体委托。使困惑

2024-04-14

早些时候我对接口的差异有了很好的理解。现在我对变体接口中委托的差异有点困惑。方法、属性、索引器和事件可以在接口中定义。变体接口中的方法、属性和索引器的签名是直接的。而事件却发生了逆转。

我不明白为什么返回值方法可以在协变接口中工作,但类似的委托签名却不起作用。不应该返回任何内容的逆变委托有效。逆变接口也是如此。

代码如下:

namespace ConsoleApp1
{
    delegate T MyDelegateReturn<out T>();
    delegate void MyDelegateTake<in T>(T t);

    interface IMyInterfaceReturn<out T>
    {
        T MyMethodReturn(); //signature with return value and interface with keyword out - ok

        //event MyDelegateReturn<T> MyEventReturn; //delegate signature with return value but is error CS1961

        event MyDelegateTake<T> MyEventTake; //delegate signature with take parameter but why it is work?
    }

    interface IMyInterfaceTake<in T>
    {
        void MyMethodTake(T t); //signature with take argument and interface with keyword in - ok

        //event MyDelegateTake<T> MyEventTake; //delegate signature with take parameter but is  error CS1961

        event MyDelegateReturn<T> MyEventReturn; //delegate signature with return value but why it is work?
    }

    class MyClass<T> : IMyInterfaceReturn<T>, IMyInterfaceTake<T>
    {
        public event MyDelegateTake<T> MyEventTake;
        public event MyDelegateReturn<T> MyEventReturn;

        public T MyMethodReturn()
        {
            return default;
        }

        public void MyMethodTake(T t)
        {

        }
    }

    class A
    {

    }

    class B : A
    {

    }

    class Program
    {
        static void Main(string[] args)
        {
            B MyMethodReturn()
            {
                return default;
            }

            void MyMethodTake(A i)
            {

            }

            MyClass<B> myClass1 = new MyClass<B>();
            myClass1.MyEventTake += MyMethodTake;

            MyClass<A> myClass2 = new MyClass<A>();
            myClass2.MyEventReturn += MyMethodReturn;
        }
    }
}

规则是事件的类型必须是输入安全。这与方法参数相同。这是有道理的,因为当您在界面减速中声明事件时,您实际上是在声明一对add and remove访问器,看起来像这样:

void add_MyEventTake(MyDelegateTake<T> value);
void remove_MyEventTake(MyDelegateTake<T> value);

当你使用时这些将会被调用+= and -=关于活动。

因此,为了简化事情,我们将考虑接受的方法MyDelegateTake and MyDelegateReturn.

interface IMyInterfaceReturn<out T>
{
    T Get();
    void add_MyEventTake(MyDelegateTake<T> t);

}

有一个section https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/interfaces#variance-safety在描述规则的 C# 规范中。如果您愿意,您可以查看规则,但我会尝试更直观地解释这一点。

在本节的最后,描述了如何直观地解释“输入安全”和“输出安全”:

直观地,在输出位置禁止输出不安全类型,并且在输入位置禁止输入不安全类型。

如果类型不是输出不安全,则它是输出安全的;如果它不是输入不安全,则它是输入安全。

In IMyInterfaceReturn, T是协变的,因此输出安全,因此只能在“输出位置”使用。我现在将显示该参数MyDelegateTake<T>确实是一个“输出位置”。

假设你有一个实现IMyInterfaceReturn<string> called Foo:

class Foo : IMyInterfaceReturn<string>
{
    public void add_MyEventTake(MyDelegateTake<string> t)
    {
        t(Get());
    }

    public string Get()
    {
        return "Something produced by Foo";
    }
}

您可以使用Foo像这样:

var foo = new Foo();
foo.add_MyEventTake(str => /* do something with str */);

你不同意吗str是某种“输出”Foo?上面的代码实际上就是以下内容,但以一种更间接的方式:

var foo = new Foo();
var str = foo.Get();
// do something with str...

因此,可以得出T处于输出位置,您可以安全地执行诸如分配之类的操作foo到一个类型的变量IMyInterfaceReturn<object>.

If add_MyEventTake took a MyDelegateReturn然而,add_MyEventTake将是一个consumer of T, and T将处于输入位置。Foo可以这样使用:

var foo = new Foo();
foo.add_MyEventTake(() => "Some Input For Foo!");
// this is just a roundabout way of doing:
foo.add_MyEventTake("Some Input Fo Foo!");

并假设Foo是这样实现的:

class Foo : IMyInterfaceReturn<string>
{
    private string s;

    public void add_MyEventTake(MyDelegateReturn<string> t)
    {
        s = t();
    }

    public string Get()
    {
        return s.ToUpper();
    }
}

那么这段代码就会崩溃:

IMyInterfaceReturn<object> foo = new Foo();
foo.add_MyEventTake(() => new object());
foo.Get(); // what would this do? object has no ToUpper!?
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

变体接口中的变体委托。使困惑 的相关文章

  • 为什么基类必须有一个带有 0 个参数的构造函数?

    这不会编译 namespace Constructor0Args class Base public Base int x class Derived Base class Program static void Main string a
  • Blazor 与 Razor

    随着 Blazor 的发明 我想知道这两种语言之间是否存在显着的效率 无论是在代码创建方面还是在代码的实际编译 执行方面 https github com SteveSanderson Blazor https github com Ste
  • 处理 fanart.tv Web 服务响应 JSON 和 C#

    我正在尝试使用 fanart tv Webservice API 但有几个问题 我正在使用 Json Net Newtonsoft Json 并通过其他 Web 服务将 JSON 响应直接反序列化为 C 对象 这里的问题是元素名称正在更改
  • 在 Xcode4 中使用 Boost

    有人设置 C Xcode4 项目来使用 Boost 吗 对于一个简单的 C 控制台应用程序 我需要在 Xcode 中设置哪些设置 Thanks 用这个来管理它 和这个
  • 调试内存不足异常

    在修复我制作的小型 ASP NET C Web 应用程序的错误时 我遇到了 OutOfMemoryException 没有关于在哪里查看的提示 因为这是一个编译时错误 如何诊断此异常 我假设这正是内存分析发挥作用的地方 有小费吗 Thank
  • VS30063:您无权访问 https://dev.azure.com

    我正在尝试在 asp net core 2 1 mvc 应用程序中使用以下代码连接 Azure DevOps Uri orgUrl new Uri https dev azure com xxxxx String personalAcces
  • 禁用 LINQ 上下文的所有延迟加载或强制预先加载

    我有一个文档生成器 目前包含约 200 个项目的查询 但完成后可能会超过 500 个 我最近注意到一些映射表示延迟加载 这给文档生成器带来了一个问题 因为它需要根据生成的文档来访问所有这些属性 虽然我知道DataLoadOptions可以指
  • 单元测试失败,异常代码为 c0000005

    我正在尝试使用本机单元测试项目在 Visual Studios 2012 中创建单元测试 这是我的测试 TEST METHOD CalculationsRoundTests int result Calculations Round 1 0
  • 两组点之间的最佳匹配

    I ve got two lists of points let s call them L1 P1 x1 y1 Pn xn yn and L2 P 1 x 1 y 1 P n x n y n 我的任务是找到它们点之间的最佳匹配 以最小化它
  • UWP 无法在两个应用程序之间创建本地主机连接

    我正在尝试在两个 UWP 应用程序之间设置 TCP 连接 当服务器和客户端在同一个应用程序中运行时 它可以正常工作 但是 当我将服务器部分移动到一个应用程序并将客户端部分移动到另一个应用程序时 ConnectAsync 会引发异常 服务器未
  • Qt - 设置不可编辑的QComboBox的显示文本

    我想将 QComboBox 的文本设置为某些自定义文本 不在 QComboBox 的列表中 而不将此文本添加为 QComboBox 的项目 此行为可以在可编辑的 QComboBox 上实现QComboBox setEditText cons
  • 过期时自动重新填充缓存

    我当前缓存方法调用的结果 缓存代码遵循标准模式 如果存在 则使用缓存中的项目 否则计算结果 在返回之前将其缓存以供将来调用 我想保护客户端代码免受缓存未命中的影响 例如 当项目过期时 我正在考虑生成一个线程来等待缓存对象的生命周期 然后运行
  • 是否有一个 C++ 库可以从 PDF 文件中提取文本,例如 PDFBox for Java? [关闭]

    Closed 此问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 去年 我使用 PDFBox 在 Java 中创建了一个应用程序来获取某些 PDF 文件中的原始文本 现在
  • Fluent NHibernate 日期时间 UTC

    我想创建一个流畅的 nhibernate 映射来通过以下方式映射 DateTime 字段 保存时 保存 UTC 值 读取时 调整为本地时区值 实现此映射的最佳方法是什么 就我个人而言 我会将日期存储在 UTC 格式的对象中 然后在读 写时在
  • 我应该在应用程序退出之前运行 Dispose 吗?

    我应该在应用程序退出之前运行 Dispose 吗 例如 我创建了许多对象 其中一些对象具有事件订阅 var myObject new MyClass myObject OnEvent OnEventHandle 例如 在我的工作中 我应该使
  • 过度使用委托对性能来说是一个坏主意吗? [复制]

    这个问题在这里已经有答案了 考虑以下代码 if IsDebuggingEnabled instance Log GetDetailedDebugInfo GetDetailedDebugInfo 可能是一个昂贵的方法 因此我们只想在调试模式
  • 为什么 Ajax.BeginForm 在 Chrome 中不起作用?

    我正在使用 c NET MVC2 并尝试创建一个 ajax 表单来调用删除数据库记录 RemoveRelation 的方法 删除记录的过程正在按预期进行 删除记录后 表单应调用一个 JavaScript 函数 从视觉效果中删除该记录 Rem
  • 热重载时调用方法

    我正在使用 Visual Studio 2022 和 C 制作游戏 我想知道当您热重新加载应用程序 当它正在运行时 时是否可以触发一些代码 我基本上有 2 个名为 UnloadLevel 和 LoadLevel 的方法 我想在热重载时执行它
  • 在基类集合上调用派生方法

    我有一个名为 A 的抽象类 以及实现 A 的其他类 B C D E 我的派生类持有不同类型的值 我还有一个 A 对象的列表 abstract class A class B class A public int val get privat
  • 如何创建向后兼容 Windows 7 的缩放和尺寸更改每显示器 DPI 感知应用程序?

    我是 WPF 和 DPI 感知 API 的新手 正在编写一个在 Windows 7 8 1 和 10 中运行的应用程序 我使用具有不同每个显示器 DPI 设置的多个显示器 并且有兴趣将我的应用程序制作为跨桌面配置尽可能兼容 我已经知道可以将

随机推荐