草庐IT

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

coder 2024-05-24 原文

声明是什么意思?

From here

ref and out parameters in C# and cannot be marked as variant.



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”的意思是,粗略地说,“只出现在输入位置”。

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

考虑接口(interface)的方法或委托(delegate)表示的方法:

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

T 是否出现在输入位置?是的。调用者可以通过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"之间的唯一区别是编译器禁止在被调用者分配之前读取 out 参数,并且编译器要求在被调用者正常返回之前进行分配。使用 C# 以外的 .NET 语言编写此接口(interface)的实现的人将能够在项目初始化之前读取该项目,因此可以将其用作输入。因此,我们禁止在这种情况下将 T 标记为“out”。这令人遗憾,但我们对此无能为力;我们必须遵守 CLR 的类型安全规则。

此外,“out”参数的规则是它们在写入之前不能用于输入。没有规定它们在写入后不能用于输入。假设我们允许
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作为形式参数声明,这意味着我们现在有 inout意思是两件事;这会造成一些困惑。让我澄清一下:
  • in , outref在参数列表中的形式参数声明上意味着“此参数是调用者提供的变量的别名”。
  • ref表示“被调用者可以读取或写入别名变量,并且必须在调用之前知道它被赋值。
  • out意味着“被调用者必须通过别名写入别名变量,然后才能正常返回”。这也意味着被调用者在写入之前不能通过别名读取别名变量,因为变量可能没有明确分配。
  • in意味着“被调用者可以读取别名变量,但不会通过别名写入”。 in的目的是为了解决一个罕见的性能问题,即必须“按值”传递大型结构,但这样做的成本很高。作为实现细节,in参数通常通过指针大小的值传递,这比按值复制要快,但在取消引用时要慢。
  • 从 CLR 的角度来看,in , outref都是一样的;关于谁在什么时间读取和写入什么变量的规则,CLR 不知道也不关心。
  • 由于是 CLR 强制执行有关变化的规则,因此适用于 ref 的规则也适用于inout参数。

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

    如上所述,我们选择了 inout对于那些修饰符,因为如果我们看到 IFoo<in T, out U>然后 T用于“输入”位置和 U用于“输出”位置。虽然严格来说这不是真的,但在 99.9% 的用例中确实如此,它是一个有用的助记符。

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

    这只是在逻辑上应该协同工作的两个特性由于实现细节的原因不能很好地协同工作的奇怪的、意外的情况之一。

    关于c# - C# 中的 ref 和 out 参数不能被标记为变体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2876315/

    有关c# - C# 中的 ref 和 out 参数不能被标记为变体的更多相关文章

    1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    2. ruby - 其他文件中的 Rake 任务 - 2

      我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

    3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

      作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

    4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

      Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

    5. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

      我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

    6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    7. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

      exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

    8. ruby - RSpec - 使用测试替身作为 block 参数 - 2

      我有一些Ruby代码,如下所示:Something.createdo|x|x.foo=barend我想编写一个测试,它使用double代替block参数x,这样我就可以调用:x_double.should_receive(:foo).with("whatever").这可能吗? 最佳答案 specify'something'dox=doublex.should_receive(:foo=).with("whatever")Something.should_receive(:create).and_yield(x)#callthere

    9. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

      刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

    10. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

      我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

    随机推荐