C# 中的 ref 和 out 参数不能标记为变体

2023-12-12

该声明的含义是什么?

从这里

C# 中的 ref 和 out 参数以及 不能被标记为变体。

1)是否意味着不能进行以下操作。

public class SomeClass<R, A>: IVariant<R, A>
{
    public virtual R DoSomething( ref A args )
    {
        return null;
    }
}

2)或者这是否意味着我不能拥有以下内容。

public delegate R Reader<out R, in A>(A arg, string s);

public static void AssignReadFromPeonMethodToDelegate(ref Reader<object, Peon> pReader)
{
    pReader = ReadFromPeon;
}

static object ReadFromPeon(Peon p, string propertyName)
    {
        return p.GetType().GetField(propertyName).GetValue(p);
    }

static Reader<object, Peon> pReader;

static void Main(string[] args)
    {
        AssignReadFromPeonMethodToDelegate(ref pReader);
        bCanReadWrite = (bool)pReader(peon, "CanReadWrite");

        Console.WriteLine("Press any key to quit...");
        Console.ReadKey();
    }

我尝试了(2)并且成功了。


粗略地说,“out”的意思是“仅出现在输出位置”。

粗略地说,“in”的意思是“仅出现在输入位置”。

真实的故事比这要复杂一些,但选择关键词是因为大多数情况都是这样。

考虑接口的方法或委托表示的方法:

delegate void Foo</*???*/ T>(ref T item);

T出现在输入位置吗?是的。调用者可以在via item中传递T的值;被调用者 Foo 可以读取它。因此 T 不能被标记为“out”。

T出现在输出位置吗?是的。被调用者可以向 item 写入一个新值,然后调用者可以读取该值。因此 T 不能标记为“in”。

因此,如果 T 出现在“ref”形式参数中,则 T 不能被标记为 in 或 out。

让我们看一些实际的例子来说明问题是如何发生的。假设这是合法的:

delegate void X<out T>(ref T item);
...
X<Dog> x1 = (ref Dog d)=>{ d.Bark(); }
X<Animal> x2 = x1; // covariant;
Animal a = new Cat();
x2(ref a);

好吧,狗我的猫,我们刚刚发出了猫叫声。 “出去”不可能是合法的。

那么“在”呢?

delegate void X<in T>(ref T item);
...
X<Animal> x1 = (ref Animal a)=>{ a = new Cat(); }
X<Dog> x2 = x1; // contravariant;
Dog d = new Dog();
x2(ref d);

我们只是将一只猫放入一个只能容纳狗的变量中。 T 也不能标记为“in”。

那么输出参数呢?

delegate void Foo</*???*/T>(out T item);

?现在T只出现在输出位置。将 T 标记为“out”是否合法?

很不幸的是,不行。 “out”实际上与幕后的“ref”没有什么不同。 “out”和“ref”之间的唯一区别是compiler禁止在被调用者分配之前读取输出参数,并且编译器要求在被调用者正常返回之前进行分配。有人编写了该接口的实现使用 C# 以外的 .NET 语言能够在初始化之前读取该项目,因此可以将其用作输入。因此,在这种情况下,我们禁止将 T 标记为“out”。这是令人遗憾的,但我们对此无能为力;我们必须遵守 CLR 的类型安全规则。

此外,“out”参数的规则是它们不能用于输入在它们被写入之前。没有规定不能用于输入after他们被写给。假设我们允许

delegate void X<out T>(out T item);
class C
{
    Animal a;
    void M()
    {
        X<Dog> x1 = (out Dog d) => 
        { 
             d = null; 
             N(); 
             if (d != null) 
               d.Bark(); 
        };
        x<Animal> x2 = x1; // Suppose this were legal covariance.
        x2(out this.a);
    }
    void N() 
    { 
        if (this.a == null) 
            this.a = new Cat(); 
    }
}

我们再一次发出了猫叫声。我们不能让T“出局”。

以这种方式使用out参数进行输入是非常愚蠢的,但却是合法的。


更新:C# 7 已添加in作为形式参数声明,这意味着我们现在拥有in and out意味着两件事;这会造成一些混乱。让我澄清一下:

  • in, out and ref on a 参数列表中的形式参数声明意思是“这个参数是调用者提供的变量的别名”。
  • ref意思是“被调用者可以读取或写入别名变量,并且必须在调用之前知道它已被赋值。
  • out意思是“被调用者必须通过别名写入别名变量才能正常返回”。这也意味着被调用者在写入别名变量之前不得通过别名读取别名变量,因为该变量可能未明确分配。
  • in意思是“被调用者可以读取别名变量,但不能通过别名写入它”。的目的in是为了解决一个罕见的性能问题,即必须“按值”传递大型结构,但这样做的成本很高。作为实施细节,in参数通常通过指针大小的值传递,这比按值复制更快,但在取消引用时速度较慢。
  • 从 CLR 的角度来看,in, out and ref都是同一件事;关于谁在什么时间读取和写入什么变量的规则,CLR 不知道也不关心。
  • 由于 CLR 强制执行有关方差的规则,因此适用于ref也适用于in and out参数。

相比之下,in and outon 类型参数声明分别表示“此类型参数不得以协变方式使用”和“此类型参数不得以逆变方式使用”。

如上所述,我们选择了in and out对于这些修饰符,因为如果我们看到IFoo<in T, out U> then T用于“输入”位置并且U用于“输出”位置。虽然那不是strictly确实,在 99.9% 的用例中,它确实是一个有用的助记符。

不幸的是interface IFoo<in T, out U> { void Foo(in T t, out U u); }是非法的,因为它看起来应该有效。它无法工作,因为从 CLR 验证程序的角度来看,这些都是ref参数,因此可读写。

这只是那些奇怪的、意想不到的情况之一,逻辑上应该协同工作的两个功能由于实现细节原因而不能很好地协同工作。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C# 中的 ref 和 out 参数不能标记为变体 的相关文章

  • “字符串”是什么意思?信息'

    我刚刚在查看定义时发现了这个PlatformNotSupportedException class 什么是string message意思是 据我所知是 是缩写Nullable lt gt but Nullable lt gt 只能应用于结
  • c# - 显示小数点到小数点后 6 位 [重复]

    这个问题在这里已经有答案了 可能的重复 具有 N 个小数位的 Double ToString https stackoverflow com questions 3059759 double tostring with n number o
  • dup2() 和 exec()

    include
  • 如何通知父线程所有子线程都已终止?

    我有一个控制台应用程序正在移植到 WPF 该应用程序有 3 个工作线程 在将一些输出结果打印到屏幕上之前 这些线程都连接到主线程 我的理解是 如果我尝试在 WPF 应用程序中执行相同的操作 GUI 将被阻止并且不会响应用户 那么如何通知父线
  • C++ 和序列化:有什么方法可以进行某种内省吗?

    我读过一些例子维基百科 http en wikipedia org wiki Type introspection C 2B 2B但我正在寻找一些现实生活中的例子 如何使用内省 为什么 它有助于编写干净的代码 以及代码本身 例如 有没有办法
  • 为什么这个 oracle 批量插入不起作用?

    我正在尝试将一些数据批量插入到 oracle 数据库中 我按照文档中的示例进行操作 this DataBaseAccess new OracleConnection connString var dataAdapter new Oracle
  • C# 并行与并行线程代码性能

    我一直在测试 System Threading Parallel 与线程的性能 我很惊讶地发现并行比线程花费更长的时间来完成任务 我确信这是由于我对并行的了解有限 我刚刚开始阅读 我想我会分享一些片段 如果有人可以向我指出并行代码比线程代码
  • WPF Dispatchertimer 延迟反应/冻结

    在我的 WPF 应用程序中 我使用 3 个不同的 DispatcherTimers 一种是用于显示当前时间 一种是每 5 秒运行一次数据库查询 第三个每 1 秒刷新一次自定义按钮的值 当我的程序运行时 有很多延迟 冻结 例如 时间开始正确计
  • & 运算符的含义是什么?

    在下面的代码中 Expression
  • 多维数组和指向指针的指针

    创建多维数组时char a 10 10 根据我的书 它说你必须使用类似于char a 10 将数组传递给函数 为什么必须这样指定长度 您不是只是将双指针传递给 with 并且该双指针不是已经指向分配的内存吗 那么为什么参数不能是char a
  • 以编程方式运行 T4 文本模板

    有没有一种方法可以通过代码以编程方式运行 T4 文本模板 我正在制作一种自定义域特定语言 我希望相关的文本模板在用户每次保存时运行 目前 这就是我在 DSL 模型中所做的事情 protected override void OnDocume
  • 函数指针上的未知类型 F TYPE

    include
  • 从套接字读取 C HTTP

    我想知道如何判断是否已从套接字接收到所有数据 这是一个简单的网络代理 现在我正在处理请求部分 所以发送的内容应该以 r n r n 结尾 我不知道请求会持续多久 我在这里读过一些帖子 说我应该检查读取函数是否返回 0 但其他人说0只在客户端
  • 如何从Web JavaScript应用程序获取桌面C#程序中的变量

    我遇到一个问题 有两个应用程序 一种是 C 中的桌面应用程序 另一种是 javascript 中的 Web 应用程序 运行桌面应用程序中的一些变量或信息需要传输到Web应用程序 有谁知道如何解决这个问题 有人愿意提供更多细节来解决这个问题吗
  • 三种 System.Drawing 方法表现出缓慢的绘制或闪烁:解决方案?或其他选择?

    我正在通过 System Drawing 进行一些绘图 但遇到了一些问题 我将数据保存在队列中 并将该数据绘制 绘制 到三个图片框中 此方法填充图片框 然后滚动图形 所以不要在以前的绘图上绘制 并且逐渐看起来更混乱 我找到了两种绘制图表的解
  • C++:LPWSTR 在 cout 中打印为地址

    我有一个类型变量LPTSTR 我打印到std cout with lt lt 在 ANSI 系统中 不知道它是在哪里确定的 它工作得很好 它打印了字符串 现在 在 Unicode 系统中 我得到的是十六进制地址而不是字符串 那么 为什么LP
  • 从视图模型调用方法的命令

    好吧 我倾向于避免使用命令 因为它们总是让我感到困惑 但我正在进行一个新项目 并且正在尝试正确构建它 并且在我看来没有任何代码隐藏 基本上我现在想做的就是连接一个按钮来触发一个命令 在我的视图模型上执行一些操作 但不知何故 如此简单的事情仍
  • Request.Form 和 Request.QueryString 之间的区别?

    有人可以告诉我两者之间的确切区别吗Request Form and Request QueryString 我知道一个区别 比如 如果HTTP请求方式为POST 则用户提交的数据在申请表 收藏 如果HTTP请求方法是GET 则用户提交的数据
  • 为什么必须通过 this 指针访问模板基类成员?

    如果下面的类不是模板 我可以简单地拥有x in the derived班级 但是 通过下面的代码 我have to use this gt x Why template
  • 使用 _Alignas 进行结构成员对齐

    我想知道以下问题 是新的吗 Alignas结盟 C11 中的说明符适用于结构成员吗 我一直假设这么多 但彻底阅读了 N1570 公开草案似乎表明对齐说明符不能 出现在一个说明符限定符列表 这就是我所期望的 如果得到支持的话 我已经读过几遍语

随机推荐