C#:指向多个整数的引用/指针数组

发布于 2024-10-16 01:52:15 字数 627 浏览 2 评论 0原文

我想在数组中保存对许多短裤的引用。我以为我可以创建短裤,然后将它们添加到数组中。所以...每次引用的对象发生更改时,都会反映在数组中,反之亦然。经过一些试验后,我确信事实并非如此。事实上,看起来只是传递了值,而不是引用。

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

        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 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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

童话 2024-10-23 01:52:15

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

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

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

短裤是价值类型。

如果您希望 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);

就这样。

现在,您将获得对简短内容的引用访问权限,因为简短内容实际上存储在与类的 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”元素类型的数组或存储引用的字段。所以从这个意义上说,你无法做你想做的事。

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

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.

为你拒绝所有暧昧 2024-10-23 01:52:15

Short 是一个值类型,但是您试图使其表现得像引用类型

您可以创建一个带有 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 实现转换器> 到你的包装类并返回)。

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

青朷 2024-10-23 01:52:15

short 类型是 值类型并且不像引用类型那样工作,引用类型的行为就像您期望的那样。当您将值类型分配给变量时,将分配其值,而不是其引用。 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也会改变。

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.

宣告ˉ结束 2024-10-23 01:52:15

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

另一种(也是更正确的)看待它的方式是,shorts、ints 等是不可变的 =>它们无法改变。所以你基本上不能改变短路。如果您需要在某处更改 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 类型的对象。

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

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.

埋葬我深情 2024-10-23 01:52:15

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

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

尝试以下操作:

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

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; } }
铃予 2024-10-23 01:52:15

如果您向类添加转换运算符,则可以透明地使用 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 限定符而不是 隐式,如果您想让事情变得更清晰,但代价是有点冗长,您可以要求对这些转换进行强制转换。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文