C#: Array of references / pointers to a number of integers

与世无争的帅哥 提交于 2019-12-18 06:57:37

问题


I would like to hold references to a number of shorts in an array. I assumed I could just create the shorts and then add them to the array. So... every time the referenced object is changed, this is reflected in the array, and vice versa. Doing some trials convinced me that it does not quite work that way. In fact, it looks like the value is transferred but not a reference.

Below code creates two shorts, adds these to an array as objects, then changes the original short. However, when accessing the assumed referenced short in the array it has not changed, which leads me to believe that it is a wholly new object that has nothing to do with the original one.

        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]);

I am misunderstanding something fundamental here and would appreciate if someone could explain, and perhaps point me to a solution that would do what I want.


回答1:


There are two kinds of types in the C# type system "value types" and "reference types".

Value types are copied by value; when you copy one, you get a wholly new object that has nothing to do with the original.

Reference types are copied by reference; when you copy one, you are actually copying a reference to some storage location. You get two references that both refer to one object.

Shorts are value types.

If you want a short to be a reference type, then you could make a reference type wrapper:

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);

And there you go.

Now, that will give you reference access to the short because the short is actually stored in the field associated with the value property of the class. If you then say:

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

you won't get "3" -- v2 now refers to a different object than vs[1]. If what you really want to capture is a reference to a variable then what you want to use is a closure.

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

Here we capture in the array objects which know how to get and set the current values of v1 and v2.

Now, if what you want to do is make an alias to another variable directly, without this object in the way, then there is only one way to do that in C#:

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

Now "x" and "y" are two names for the same variable. However, the concept of "make an alias to another variable" only works in C# when the aliasing variable is a formal parameter of a method. There is no way to do it in general.

Now, we could in theory do something like what you want. We could support "ref locals":


UPDATE: The "theoretical" feature I discuss here was added to C# 7.0.


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

That is, rv1 becomes an alias for v1. C# does not support this, but the CLR does and therefore we could support it. However, the CLR does not support making arrays of "ref" element type, or fields that store refs. So in that sense, you couldn't do what you want.

C# does support some special "hidden" features for passing around objects that act like references to variables but are lighter weight than the "two delegate" reference mentioned above. However, these special features are only for bizarre interop scenarios and I recommend against them. (And again, you can't make an array that stores typed references.) I don't think I'll talk about those features more in this answer; you really don't want to go there, believe me.




回答2:


Short is a value type, but you're trying to make it behave like a reference type.

You can create a class with a short property and then use an array of that class:

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;
   }
}

There's potentially some work you can do there to make it smoother (like implementing a converter from short to your wrapper class and back).




回答3:


The short type is a value type and does not work like reference types which behaves like you are expecting your shorts to behave. When you assign a value type to a variable, its value is assigned, not its reference. vs[0] will hold a copy of the value you assigned to v1.

If you really need to have the values in the array change when you change the original value, you need to wrap your short in a reference type. Here is an example:

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

Then you can use it like this:

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

If you change v1.Value, then shortArray[0].Value will also change.




回答4:


Value types are called value types because they are passed by value when passed to methods or assigned via the = operator.

Another (and more correct) way to look at it is that shorts, ints, etc. are immutable => they cannot be changed. So you basically cannot change a short. If you need an object of type short to change somewhere you need to create a class to hold this object like this:


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);
    }
}

Essentially the code is replacing the object of type short with a new object.

BTW chances are that there is something wrong with your design if you need to wrap a naked short. You either should be using some more complex object already or should be working with the array of shorts in some other way. But I guess you are just testing.




回答5:


The fundamental problem is that short is a struct and not an object. So basically an array of short is actually an array of short and not an array of references to short objects.

To solve the problem you can "box" the short in a class (but it's going to be tedious)

Try with the following:

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



回答6:


You can use ReferenceType transparently as if float, int etc. were actually reference types if you add a conversion operator to the class:

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;

By using the explicit qualifier instead of implicit, you can require casts for these conversions, if you want to make things clearer at the expense of a little verbosity.



来源:https://stackoverflow.com/questions/4900383/c-array-of-references-pointers-to-a-number-of-integers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!