Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/28.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ref返回C#7.0中的限制_C#_C# 7.0 - Fatal编程技术网

Ref返回C#7.0中的限制

Ref返回C#7.0中的限制,c#,c#-7.0,C#,C# 7.0,我试图理解以下摘自一篇关于C#7.0中与ref返回相关的新特性的官方博客文章 只能返回“可安全返回”的引用:已安全返回的引用 传递给您,以及指向对象中字段的对象。 Ref局部变量被初始化到某个存储位置,不能变异为指向另一个存储位置。 不幸的是,博客文章没有给出任何代码示例。如果有人能通过实际例子和解释,对以粗体突出显示的限制进行更多说明,我将不胜感激 提前感谢。您可以在上找到有关此功能的精彩讨论 1。您只能返回“安全返回”的引用:那些 传递给您,以及指向对象中字段的 以下示例显示安全引用的返

我试图理解以下摘自一篇关于C#7.0中与ref返回相关的新特性的官方博客文章

  • 只能返回“可安全返回”的引用:已安全返回的引用 传递给您,以及指向对象中字段的对象。

  • Ref局部变量被初始化到某个存储位置,不能变异为指向另一个存储位置。

  • 不幸的是,博客文章没有给出任何代码示例。如果有人能通过实际例子和解释,对以粗体突出显示的限制进行更多说明,我将不胜感激


    提前感谢。

    您可以在上找到有关此功能的精彩讨论

    1。您只能返回“安全返回”的引用:那些 传递给您,以及指向对象中字段的

    以下示例显示安全引用的返回,因为它来自调用者:

    public static ref TValue Choose<TValue>(ref TValue val)
    {
        return ref val;
    }
    
    将不编译。您也无法将新引用分配给已存在的引用,如

    aReference = ref anOtherValue;
    

    要通过引用传递某物,必须将其分类为变量。C#规范(§5变量)定义了七类变量:静态变量、实例变量、数组元素、值参数、引用参数、输出参数和局部变量

    class ClassName {
        public static int StaticField;
        public int InstanceField;
    }
    void Method(ref int i) { }
    void Test1(int valueParameter, ref int referenceParameter, out int outParameter) {
        ClassName instance = new ClassName();
        int[] array = new int[1];
        outParameter=0;
        int localVariable = 0;
        Method(ref ClassName.StaticField);  //Static variable
        Method(ref instance.InstanceField); //Instance variable
        Method(ref array[0]);               //Array element
        Method(ref valueParameter);         //Value parameter
        Method(ref referenceParameter);     //Reference parameter
        Method(ref outParameter);           //Output parameter
        Method(ref localVariable);          //Local variable
    }
    
    第一点实际上是说,您可以引用分类为引用参数、输出参数、静态变量和实例变量的返回变量

    ref int Test2(int valueParameter, ref int referenceParameter, out int outParameter) {
        ClassName instance = new ClassName();
        int[] array = new int[1];
        outParameter=0;
        int localVariable = 0;
        return ref ClassName.StaticField;  //OK, "ones that point into fields in objects"
        return ref instance.InstanceField; //OK, "ones that point into fields in objects"
        return ref array[0];               //OK, array elements are also "safe to return" by reference
        return ref valueParameter;         //Error
        return ref referenceParameter;     //OK, "ones that were passed to you"
        return ref outParameter;           //OK, "ones that were passed to you"
        return ref localVariable;          //Error
    }
    

    注意,例如,值类型的字段,应该考虑“安全返回”封闭变量的状态。不总是允许这样做,例如引用类型的实例字段:

    struct StructName {
        public int InstacneField;
    }
    ref int Test3() {
        StructName[] array = new StructName[1];
        StructName localVariable = new StructName();
        return ref array[0].InstacneField;      //OK, array[0] is "safe to return"
        return ref localVariable.InstacneField; //Error, localVariable is not "safe to return"
    }
    
    ref return方法的结果被视为“安全返回”,如果此方法不接受任何非“安全返回”参数:

    ref int ReturnFirst(ref int i, ref int ignore) => ref i;
    ref int Test4() {
        int[] array = new int[1];
        int localVariable = 0;
        return ref ReturnFirst(ref array[0], ref array[0]);      //OK, array[0] is "safe to return"
        return ref ReturnFirst(ref array[0], ref localVariable); //Error, localVariable is not "safe to return"
    }
    
    虽然我们知道
    ReturnFirst(ref array[0],ref localVariable)
    将返回“safe to return”引用(
    ref array[0]
    ),但编译器无法通过单独分析
    Test4
    方法来推断它。因此,在这种情况下,
    ReturnFirst
    方法的结果被视为不“安全返回”

    第二点是,ref局部变量声明必须包括初始值设定项:

    int localVariable = 0;
    ref int refLocal1;                     //Error, no initializer
    ref int refLocal2 = ref localVariable; //OK
    
    此外,ref局部变量不能重新分配到指向其他存储位置:

    int localVariable1 = 0;
    int localVariable2 = 0;
    ref int refLocal = ref localVariable1;
    ref refLocal = ref localVariable2;     //Error
    refLocal = ref localVariable2;         //Error
    

    实际上,没有有效的语法来重新分配ref局部变量。

    您得到了一些答案,澄清了限制,但没有解释限制背后的原因

    限制背后的理由是,我们决不能允许死变量使用别名。如果在一个普通方法中有一个普通的局部,并且返回一个对它的引用,那么在使用该引用时,该局部是死的

    现在,有人可能会指出,由ref返回的local可以被提升到闭包类的字段中。是的,那会解决问题。但该功能的要点是允许开发人员编写高性能、接近机器的低成本机制,并自动提升到关闭状态,然后承担收集压力等负担,以实现这一目标

    事情可能会变得有点棘手。考虑:

    ref int X(ref int y) { return ref y; }
    ref int Z( )
    {
      int z = 123;
      return ref X(ref z);
    }
    
    在这里,我们以一种偷偷摸摸的方式将一个参考返回到本地z!这也必须是非法的。但是现在考虑一下:

    ref double X(ref int y) { return ref whatever; }
    ref double Z( )
    {
      int z = 123;
      return ref X(ref z);
    }
    
    现在我们可以知道返回的ref不是
    z
    的ref。那么,如果传入的ref的类型与返回的ref的类型都不同,我们能说这是合法的吗

    这个怎么样

    struct S { public int s; }
    ref int X(ref S y) { return ref y.s; }
    ref int Z( )
    {
      S z = default(S);
      return ref X(ref z);
    }
    
    现在我们再次返回了一个死变量的ref

    当我们第一次设计此功能时(在2010年IIRC),有许多复杂的建议来处理这些情况,但我最喜欢的建议只是“使所有这些都非法”。您不能返回由ref返回方法返回的引用,即使它不可能是死的


    我不知道C#7团队最终执行了什么规则。

    本页上的其他答案是完整和有用的,但我想补充一点,那就是
    out
    参数(函数需要完全初始化)被计算为“安全返回”,以便

    ref int Test2(int valueParameter, ref int referenceParameter, out int outParameter) {
        ClassName instance = new ClassName();
        int[] array = new int[1];
        outParameter=0;
        int localVariable = 0;
        return ref ClassName.StaticField;  //OK, "ones that point into fields in objects"
        return ref instance.InstanceField; //OK, "ones that point into fields in objects"
        return ref array[0];               //OK, array elements are also "safe to return" by reference
        return ref valueParameter;         //Error
        return ref referenceParameter;     //OK, "ones that were passed to you"
        return ref outParameter;           //OK, "ones that were passed to you"
        return ref localVariable;          //Error
    }
    
    有趣的是,将这一事实与另一个新的C#7功能相结合,可以模拟局部变量的通用内联声明功能:

    辅助功能:

    public static class _myglobals
    {
        /// <summary> Helper function for declaring local variables inline. </summary>
        public static ref T local<T>(out T t)
        {
            t = default(T);
            return ref t;
        }
    };
    
    using System;
    using /* etc... */
    using System.Xml;
    using Microsoft.Win32;
    
    using static _myglobals;    //  <-- puts function 'local(...)' into global name scope
    
    namespace MyNamespace
    {
       // ...
    
    var s = "abc" + (local(out int i) = 2) + "xyz";   //   <-- inline declaration of local 'i'
    i++;
    Console.WriteLine(s + i);   //   -->  abc2xyz3
    
    if ((local(out OpenFileDialog dlg) = new OpenFileDialog       // <--- inline local 'dlg'
        {
            InitialDirectory = Environment.CurrentDirectory,
            Title = "Pick a file",
        }).ShowDialog() == true)
    {
        MessageBox.Show(dlg.FileName);
    }
    
    演练:局部变量
    d
    为“内联声明”,并根据offs字段计算第一级比较的结果进行初始化。如果此结果不确定,则返回第二级排序(基于大小字段)。但是在另一种情况下,我们仍然可以返回第一级结果,因为它保存在本地
    d

    请注意,上述操作也可以在没有辅助功能的情况下通过C#7完成:

    包括在源文件的顶部:

    public static class _myglobals
    {
        /// <summary> Helper function for declaring local variables inline. </summary>
        public static ref T local<T>(out T t)
        {
            t = default(T);
            return ref t;
        }
    };
    
    using System;
    using /* etc... */
    using System.Xml;
    using Microsoft.Win32;
    
    using static _myglobals;    //  <-- puts function 'local(...)' into global name scope
    
    namespace MyNamespace
    {
       // ...
    
    var s = "abc" + (local(out int i) = 2) + "xyz";   //   <-- inline declaration of local 'i'
    i++;
    Console.WriteLine(s + i);   //   -->  abc2xyz3
    
    if ((local(out OpenFileDialog dlg) = new OpenFileDialog       // <--- inline local 'dlg'
        {
            InitialDirectory = Environment.CurrentDirectory,
            Title = "Pick a file",
        }).ShowDialog() == true)
    {
        MessageBox.Show(dlg.FileName);
    }
    
    示例2:

    public static class _myglobals
    {
        /// <summary> Helper function for declaring local variables inline. </summary>
        public static ref T local<T>(out T t)
        {
            t = default(T);
            return ref t;
        }
    };
    
    using System;
    using /* etc... */
    using System.Xml;
    using Microsoft.Win32;
    
    using static _myglobals;    //  <-- puts function 'local(...)' into global name scope
    
    namespace MyNamespace
    {
       // ...
    
    var s = "abc" + (local(out int i) = 2) + "xyz";   //   <-- inline declaration of local 'i'
    i++;
    Console.WriteLine(s + i);   //   -->  abc2xyz3
    
    if ((local(out OpenFileDialog dlg) = new OpenFileDialog       // <--- inline local 'dlg'
        {
            InitialDirectory = Environment.CurrentDirectory,
            Title = "Pick a file",
        }).ShowDialog() == true)
    {
        MessageBox.Show(dlg.FileName);
    }
    
    这个毫无意义的示例代码的要点是,尽管我们没有以任何方式更改ref localvariable
    rpi
    本身(因为'ya不能),但它现在确实有了一个不同的(最终的)引用


    更严重的是,ref local现在允许的是一种我称之为值类型戳记的技术,它允许高效的IL-in循环体,需要访问值类型数组中每个元素的多个字段。通常,这是外部初始化(
    newobj
    /
    initobj
    )之间的权衡