【问题标题】:C#: Array of references / pointers to a number of integersC#:引用数组/指向多个整数的指针
【发布时间】:2011-02-04 16:29:28
【问题描述】:

我想在一个数组中保存对多个短裤的引用。我以为我可以创建短裤,然后将它们添加到数组中。所以……每次被引用的对象改变时,都会反映在数组中,反之亦然。做一些试验使我确信它不是那样工作的。事实上,看起来价值被转移但不是参考。

以下代码创建两个短裤,将它们作为对象添加到数组中,然后更改原始短裤。但是,当访问数组中假定的引用短时,它并没有改变,这让我相信它是一个全新的对象,与原始对象无关。

        Console.WriteLine("Testing simple references:");
        short v1 = 1;
        short v2 = 2;
        object[] vs = new object[2];
        vs[0] = v1;
        vs[1] = v2;
        v1 = 1024;
        v2 = 512;
        Console.WriteLine(" v1: " + (short)vs[0]);
        Console.WriteLine(" v2: " + (short)vs[1]);

我在这里误解了一些基本的东西,如果有人能解释一下,也许会为我指出一个可以满足我要求的解决方案,我将不胜感激。

【问题讨论】:

    标签: c# pointers integer reference short


    【解决方案1】:

    C#类型系统中有“值类型”和“引用类型”两种类型。

    值类型是按值复制的;当您复制一个时,您会得到一个与原始对象无关的全新对象。

    引用类型是通过引用复制的;当您复制一个时,您实际上是在复制对某个存储位置的引用。你会得到两个引用,它们都引用一个对象。

    short 是值类型。

    如果你想让一个 short 成为一个引用类型,那么你可以做一个引用类型包装器:

    class ReferenceType<T> where T : struct
    {
        public T Value { get; set }
        public ReferenceType(T value) { this.Value = value; }
    }
    
    var v1 = new ReferenceType<short>(1);
    var v2 = new ReferenceType<short>(2);
    var vs = new ReferenceType<short>[2] { v1, v2 };
    v1.Value = 1024;
    v2.Value = 512;
    Console.WriteLine(vs[0].Value);
    Console.WriteLine(vs[1].Value);
    

    你去吧。

    现在,您将获得对 short 的引用访问权限,因为 short 实际上存储在与类的 value 属性关联的字段中。如果你接着说:

    v2 = new ReferenceType<short>(3);
    Console.WriteLine(vs[1].Value);
    

    你不会得到“3”——v2 现在指的是与 vs[1] 不同的对象。如果您真正想要捕获的是对变量的引用,那么您想要使用的是闭包

    class ReferenceToVariable<T>
    {
        private Func<T> getter;
        private Action<T> setter;
        public ReferenceToVariable(Func<T> getter, Action<T> setter) 
        { 
            this.getter = getter;
            this.setter = setter;
        }
        public T Value { get { return getter(); } set { setter(value); } }
    }
    ...
    short v1 = 1;
    short v2 = 2;
    var vs = new [] 
    { 
        new ReferenceToVariable<short>(()=>v1, x=>{v1=x;}),
        new ReferenceToVariable<short>(()=>v2, x=>{v2=x;})
    };
    v1 = 123;
    vs[1].Value = 456;
    Console.WriteLine(vs[0].Value); // 123
    Console.WriteLine(v2); // 456
    

    这里我们在数组中捕获知道如何获取和设置 v1 和 v2 的当前值的对象。

    现在,如果你想做一个别名到另一个变量直接,没有这个对象的方式,那么只有一种方法可以做到这一点在 C# 中:

    void M(ref short x)
    {
        x = 123;
    }
    ...
    short y = 1;
    M(ref y);
    

    现在“x”和“y”是同一个变量的两个名称。但是,“为另一个变量创建别名”的概念仅在别名变量是方法的形式参数时才在 C# 中有效。一般没有办法做到。

    现在,理论上我们可以做你想做的事情。我们可以支持“ref locals”:


    更新:我在这里讨论的“理论”特性已添加到 C# 7.0。


    short v1 = 1;
    ref short rv1 = ref v1;
    rv1 = 123;
    Console.WriteLine(v1); // 123
    

    也就是说,rv1 成为 v1 的别名。 C# 不支持这一点,但 CLR 支持,因此我们可以支持它。但是,CLR 确实支持创建“ref”元素类型的数组或存储 ref 的字段。所以从这个意义上说,你不能做你想做的事。

    C# 确实支持一些特殊的“隐藏”功能来传递对象,这些对象的作用类似于对变量的引用,但比上面提到的“两个委托”引用更轻。但是,这些特殊功能仅适用于奇怪的互操作场景,我建议不要使用它们。 (同样,你不能创建一个存储类型引用的数组。)我认为我不会在这个答案中更多地讨论这些特性;你真的不想去那里,相信我。

    【讨论】:

    【解决方案2】:

    Shortvalue type,但您试图使其表现得像 reference type

    您可以使用short 属性创建一个类,然后使用该类的数组:

    public class MyShort
    {
        public short Value {get; set;}
    }
    
    public class SomeOtherClass
    {
       public void SomeMethod()
       {
           MyShort[] array = new MyShort[2];
           array[0] = new MyShort {Value = 5};
           array[1] = new MyShort {Value = 2};
    
           array[0].Value = 3;
       }
    }
    

    您可以在那里做一些工作以使其更平滑(例如实现一个从 short 到您的包装类并返回的转换器)。

    【讨论】:

      【解决方案3】:

      short 类型是value type,它不像引用类型那样工作,它的行为就像你期望你的短裤一样。当您将值类型分配给变量时,分配的是它的值,而不是它的引用。 vs[0] 将保存您分配给 v1 的值的副本。

      如果您确实需要在更改原始值时更改数组中的值,则需要将您的 short 包装在引用类型中。这是一个例子:

      public class ShortHolder {
        public short Value { get; set; }
      }
      

      那么你可以这样使用它:

      var v1 = new ShortHolder() { Value=123; }
      var shortArray = new ShortHolder[1];
      shortArray[0] = v1;
      

      如果你改变v1.Value,那么shortArray[0].Value也会改变。

      【讨论】:

        【解决方案4】:

        值类型之所以称为值类型,是因为它们在传递给方法或通过 = 运算符分配时按值传递。

        另一种(也是更正确的)看待它的方式是短裤、整数等是不可变的 => 它们无法更改。所以你基本上不能改变短路。如果你需要一个 short 类型的对象来改变某个地方,你需要创建一个类来保存这个对象,如下所示:

        
        public class ShortWrapper
        {
            public short ShortValue {get; set;}
        }
        class Program
        {
            static void Main(string[] args)
            {
                ShortWrapper short1 = new ShortWrapper{ ShortValue = 1};
                ShortWrapper short2 = new ShortWrapper { ShortValue = 2 };
        
                ShortWrapper[] shorts = new ShortWrapper[] { short1, short2 };
                shorts[0].ShortValue = 5;
        
                Console.WriteLine(short1.ShortValue);
            }
        }
        
        

        本质上,代码是用新对象替换 short 类型的对象。

        顺便说一句,如果您需要包裹裸短裤,您的设计可能有问题。您应该已经使用了一些更复杂的对象,或者应该以其他方式使用短裤数组。但我猜你只是在测试。

        【讨论】:

          【解决方案5】:

          根本问题是short 是一个结构体而不是一个对象。所以基本上short 的数组实际上是short 的数组,而不是对短对象的引用数组。

          要解决这个问题,你可以在课堂上“装箱”(但这会很乏味)

          尝试以下方法:

          public class MyShort { public Value { get; set; } }
          

          【讨论】:

          【解决方案6】:

          如果您向类添加转换运算符,您可以透明地使用ReferenceType,就好像float、int 等实际上是引用类型:

          class ReferenceType<T> where T : struct
          {
              public T Value { get; set; }
              public ReferenceType(T value) { this.Value = value; }
              public static implicit operator ReferenceType<T>(T b)
              {
                  ReferenceType<T> r = new ReferenceType<T>(b);
                  return r;
              }
              public static implicit operator T(ReferenceType<T> b)
              {
                  return b.Value;
              }
          }
          ReferenceType<float> f1 = new ReferenceType(100f);
          f1 = 200f;
          float f2 = f1;
          

          通过使用explicit 限定符而不是implicit,您可以要求这些转换的强制转换,如果您想以稍微冗长为代价使事情更清晰。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2017-07-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多