C #: массив ссылок / указателей на несколько целых чисел - PullRequest
3 голосов
/ 04 февраля 2011

Я хотел бы хранить ссылки на количество шорт в массиве.Я предполагал, что смогу просто создать шорты и затем добавить их в массив.Итак ... каждый раз, когда ссылочный объект изменяется, это отражается в массиве, и наоборот.Проведение некоторых испытаний убедило меня, что это не совсем так.На самом деле, похоже, что значение передается, но не ссылка.

Ниже код создает два коротких замыкания, добавляет их в массив как объекты, а затем изменяет исходное короткое замыкание.Однако при доступе к предполагаемому ссылочному короткому замыканию в массиве он не изменился, что наводит меня на мысль, что это совершенно новый объект, который не имеет ничего общего с исходным.

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

Я неправильно понимаю кое-что фундаментальное здесь и был бы признателен, если бы кто-то мог объяснить, и, возможно, указать мне решение, которое сделало бы то, что я хочу.

Ответы [ 6 ]

15 голосов
/ 04 февраля 2011

В системе типов C # существует два типа типов: «типы значений» и «ссылочные типы».

Типы значений копируются по значению; когда вы копируете один, вы получаете совершенно новый объект, который не имеет ничего общего с оригиналом.

Типы ссылок копируются по ссылке; когда вы копируете один, вы фактически копируете ссылку на какое-то место хранения. Вы получаете две ссылки, которые обе ссылаются на один объект.

Шорты являются типами значений.

Если вы хотите, чтобы шорт был ссылочным типом, вы можете создать оболочку ссылочного типа:

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

И вот, пожалуйста.

Теперь это даст вам справочный доступ к короткому значению , поскольку короткое замыкание фактически хранится в поле, связанном со свойством значения класса. Если вы тогда скажете:

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 localals":

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

То есть rv1 становится псевдонимом для v1. C # не поддерживает это, но CLR поддерживает, и поэтому мы могли бы это поддержать. Однако CLR не поддерживает создание массивов типа ref или полей, в которых хранятся ссылки. Так что в этом смысле вы не можете делать то, что хотите.

C # поддерживает некоторые специальные «скрытые» функции для передачи объектов, которые действуют как ссылки на переменные, но имеют меньший вес, чем ссылка «два делегата», упомянутая выше. Однако эти специальные функции предназначены только для странных сценариев взаимодействия, и я рекомендую против них. (И опять же, вы не можете создать массив, в котором хранятся типизированные ссылки.) Не думаю, что я расскажу об этих функциях подробнее в этом ответе; ты действительно не хочешь туда идти, поверь мне.

5 голосов
/ 04 февраля 2011

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 в класс-оболочку и обратно).

3 голосов
/ 04 февраля 2011

Тип short является типом значения и не работает как ссылочные типы, которые ведут себя так, как будто вы ожидаете, что ваши шорты будут вести себя.Когда вы назначаете тип значения переменной, присваивается ее значение, а не его ссылка.vs[0] будет содержать копию значения, которое вы присвоили v1.

Если вам действительно нужно, чтобы значения в массиве менялись при изменении исходного значения, вам нужно обернуть ваше короткое вссылочный тип.Вот пример:

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 также изменится.

1 голос
/ 04 февраля 2011

Типы значений называются типами значений, поскольку они передаются по значению при передаче в методы или присваиваются через оператор =.

Другой (и более правильный) способ взглянуть на это - шорты, целые и т. Д. Являются неизменяемыми => их нельзя изменить. Таким образом, вы в принципе не можете изменить шорт. Если вам нужен объект типа 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 новым объектом.

Кстати, есть вероятность, что с вашим дизайном что-то не так, если вам нужно обернуть голое короткое. Вы либо должны уже использовать более сложный объект, либо работать с массивом шорт каким-либо другим способом. Но я думаю, вы просто тестируете.

0 голосов
/ 03 января 2018

Вы можете использовать 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, вы можете требовать приведений для этих преобразований, если хотите прояснить ситуацию за счет небольшого многословия.

0 голосов
/ 04 февраля 2011

Основная проблема заключается в том, что short является структурой, а не объектом.Таким образом, в основном массив short на самом деле является массивом short, а не массивом ссылок на короткие объекты.

Чтобы решить проблему, вы можете "поместить" короткое замыкание в класс (нобыть утомительным)

Попробуйте следующее:

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