C# Массив ссылок на объекты Variables и/или Form

То, что я пытаюсь сделать, это иметь массив ссылок на переменные. Под этим я подразумеваю эквивалент массива C указателей на целые числа (например).

Пример: (!!не настоящий код!!)

int a = 4;
int b = 5;
int c = 6;
List<ref int> tmp = new List<ref int>();

tmp.Add(ref a);
tmp.Add(ref b);
tmp.Add(ref c);

tmp[0] = 16;
tmp[1] = 3;
tmp[2] = 1000;

Console.Writeline(tmp[0] + " " + a); // 16 16
Console.Writeline(tmp[1] + " " + b); // 3 3
Console.Writeline(tmp[2] + " " + c); // 1000 1000

Специфика моего случая: у меня есть список строк, которые будут соответствовать ключам в словаре. Я думаю, что хочу иметь список кортежей, где Type1 — это ссылка либо на int, либо на строку, а Type2 — это ссылка на текстовое поле.

Я буду перебирать этот список, используя строку для получения значения из словаря (и делая что-то с этими данными), а затем сохраняя результаты этого в Type1. И в конечном итоге я буду брать данные из этих ссылок на переменные Type1 и копировать их данные в соответствующее текстовое поле Type2.

Это суть того, что я думаю, что я хочу сделать. Если кто-то считает, что мой подход слишком сложен, я скажу, что мне нужно сохранить текстовые поля такими, какие они есть, к сожалению, поэтому я не могу просто создать массив и перебирать его. И было бы желательно также хранить переменные Type1 отдельно, хотя это и не так необходимо.

Теперь, прочитав все вокруг, я подумал, что Func‹> выглядит так, как будто это самое полезное для того, что я хочу, поэтому я попытался использовать следующее (с Type1 как объект, потому что он должен обрабатывать как целые числа, так и строки)

List<Tuple<string, Func<object>, Func<object>>>

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


person DanielCardin    schedule 19.06.2013    source источник
comment
Если кто-то считает, что мой подход слишком сложен, я скажу, что мне нужно сохранить текстовые поля такими, какие они есть, к сожалению, поэтому я не могу просто создать массив и перебирать его. - Что это значит?   -  person Adam Robinson    schedule 19.06.2013
comment
в этом случае вы можете использовать указатели, использовать небезопасное ключевое слово для метода и установить небезопасный проект, чтобы разрешить указатели в С#, также вы можете инкапсулировать значение в классе, а в С# каждый класс имеет ссылочный тип   -  person ahmedsafan86    schedule 19.06.2013
comment
@Ahmedsafan: Хотя я не согласен с целесообразностью того, что вы предлагаете, это должен быть ответ, а не комментарий.   -  person Adam Robinson    schedule 19.06.2013
comment
не уверен, правильно ли я понял ваш вопрос, но вы можете использовать проверку дженериков ссылка . Делегаты Func на самом деле не нужны, пока вы не используете асинхронную модель или модель программирования с задержкой.   -  person Rameez Ahmed Sayad    schedule 19.06.2013
comment
Хорошо, в любом случае я пытался дать решение технической проблемы независимо от ее использования.   -  person ahmedsafan86    schedule 19.06.2013
comment
@RameezAhmedSayad: делегаты Func на самом деле не нужны, пока кто-то не использует модель асинхронного или отложенного программирования. Что заставляет вас так говорить?   -  person Adam Robinson    schedule 19.06.2013
comment
@Ahmedsafan: Понятно, но комментарии не предназначены для ответов. Если у вас есть то, что, по вашему мнению, является ответом или решением (даже если оно частичное), его следует опубликовать как ответ.   -  person Adam Robinson    schedule 19.06.2013
comment
@AdamRobinson это означает, что если бы я мог превратить текстовые поля в массив полей, то мне не нужно было бы ссылаться на них по отдельности, и я мог бы просто присвоить значения уже в цикле for.   -  person DanielCardin    schedule 19.06.2013
comment
@DanielCardin: Почему вы не можете поместить их в массив или коллекцию? Это не потребует каких-либо изменений в способе их создания или размещения в форме.   -  person Adam Robinson    schedule 19.06.2013
comment
@AdamRobinson Возможно, я не рассмотрел все сценарии. Но я не нашел необходимости в этом сценарии. пожалуйста, поправьте меня, если это не так.   -  person Rameez Ahmed Sayad    schedule 19.06.2013
comment
Я никогда не говорил, что мой подход был правильным, я сказал, что это то, что я думаю, что хочу, потому что я не был уверен. Тем не менее, предлагаемые решения, похоже, дополняют то, как я это делал, а не заменяют то, что я хотел.   -  person DanielCardin    schedule 19.06.2013


Ответы (4)


То, о чем вы конкретно просите, невозможно; что было бы более подходящим (и имеет удобство для фактической работы!) Было бы разработать структуру класса вокруг того, что вы пытаетесь сделать.

Например, что-то вроде этого:

public class MyObject // Name it appropriately
{
    public object Value { get; set; }
    public string Key { get; set; }
    public TextBox TextBox { get; set; }
}

Затем в своем коде вы можете сделать что-то вроде этого...

Dictionary<string, object> values = ...
List<MyObject> objects = ...

foreach(var item in objects)
{
    item.Value = values[item.Key];

    // process your data

    item.TextBox = item.Value.ToString();
}

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

ИЗМЕНИТЬ Вот как будет выглядеть ваш пример.

MyObject a = new MyObject() { Value = 4 };
MyObject b = new MyObject() { Value = 5 };
MyObject c = new MyObject() { Value = 6 };
List<MyObject> tmp = new List<MyObject>();

tmp.Add(a);
tmp.Add(b);
tmp.Add(c);

tmp[0].Value = 16;
tmp[1].Value = 3;
tmp[2].Value = 1000;

Console.Writeline(tmp[0].Value.ToString() + " " + a.Value.ToString()); // 16 16
Console.Writeline(tmp[1].Value.ToString() + " " + b.Value.ToString()); // 3 3
Console.Writeline(tmp[2].Value.ToString() + " " + c.Value.ToString()); // 1000 1000
person Adam Robinson    schedule 19.06.2013
comment
Если, когда я создаю MyObject и устанавливаю текстовое поле равным существующему Texbox в форме, затем устанавливаю свойство textbox.Text на что-то, и это обновляет все, что было в форме, то это именно то, что я хочу. Если нет, то я не понимаю, как это поможет - person DanielCardin; 19.06.2013
comment
@DanielCardin: Да, будет. Поскольку TextBox является классом (и, следовательно, ссылочным типом), изменение свойства или любой другой информации, относящейся к экземпляру, будет видно в любом месте программы, имеющей доступ к этому экземпляру. Другими словами, он будет делать то, что вы хотите. Вы можете найти это интересным. Хотя он специально предназначен для передачи параметров, информация в нем верна для всех переменных. - person Adam Robinson; 19.06.2013
comment
и то же самое относится к Value только потому, что он в этом классе? - person DanielCardin; 19.06.2013
comment
Я не знаю, сделал ли я это неправильно, но я попробовал это, и он правильно устанавливает texbox.text, как я хочу, но, похоже, это не меняет то, что я ввожу для значения. Это означает, что с чем-то вроде нового MyObject(test, existsInt, существующийTextbox); он изменит существующий текстовый бокс, но распечатка существующегоInt не будет тем, на что я изменил значение. - person DanielCardin; 19.06.2013
comment
+1 Если вам нужно беспокоиться только об изменении свойств ссылочных типов, я бы использовал этот подход. - person Matthew Watson; 19.06.2013
comment
@MatthewWatson: этот подход не отличается от типов значений. - person Adam Robinson; 19.06.2013
comment
@DanielCardin: идея состоит в том, чтобы использовать оболочку вместо ваших локальных переменных. - person Adam Robinson; 19.06.2013
comment
@AdamRobinson Ну, я просто не понимаю. Можете ли вы показать, как ваш подход будет работать для моего первого примера, где локальная переменная int x изменяется оболочкой? Весь смысл, о котором я говорю, заключается в возможности использовать оболочку для изменения локальной переменной. - person Matthew Watson; 19.06.2013
comment
@MatthewWatson: Как я уже сказал в комментариях к вашему ответу, строго говоря, вы не меняете локальную переменную. Хотя это выглядит так в коде (и работает в основном так же, с некоторыми оговорками), на самом деле это то, что я делаю здесь. OP здесь выглядит (без обид) как относительный новичок в C #, и делать общие утверждения, например, говорить, что оболочка изменяет локальную переменную, и вводить сложность замыканий в анонимных делегатах, определенных лямбда, когда простой класс-оболочка будет работать, кажется плохим - посоветовал. - person Adam Robinson; 19.06.2013
comment
@AdamRobinson Достаточно честно. :) - person Matthew Watson; 19.06.2013
comment
@AdamRobinson Я хотел иметь возможность иметь именованные переменные, соответствующие этим вещам. Я полагаю, что это не невозможно обойти, но это затрудняет просмотр моих данных. Смысл этой структуры (насколько я этого хотел) заключался в том, чтобы просто сопоставить строку со значением в текстовом поле и служить преобразованием. В настоящее время у меня есть все эти значения в структуре, которая значительно облегчит доступ к отдельным записям, поскольку каждая из них будет иметь имя и быть свойством класса. - person DanielCardin; 19.06.2013
comment
@DanielCardin: Вы можете; вы просто называете экземпляр MyClass (пожалуйста, назовите его как-нибудь более подходящим образом!). Смотрите мой пример. Кроме того, под структурой вы подразумеваете class или struct? - person Adam Robinson; 19.06.2013
comment
он уже переименован;) ну, прямо сейчас у меня есть класс, который представляет собой каждую строку/целое число, за которыми я отслеживаю, где каждое из них является свойством. Я полагаю, чтобы сохранить его с отдельными переменными, я мог бы просто поместить этот список туда и изменить все int/strings на экземпляры MyObject. РЕДАКТИРОВАТЬ: за исключением того, что в этом классе я не мог ссылаться на свои текстовые поля, если я не добавлю метод добавления в класс - person DanielCardin; 19.06.2013

Вы не можете хранить ссылки с помощью C#. Вы можете использовать только ключевое слово ref при вызове метода.

Вы можете использовать указатели, но только с выражением fixed и в контексте unsafe.

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

Во-первых, напишите класс «оболочки значений» следующим образом:

public class ValueWrapper<T>
{
    readonly Func<T>   get;
    readonly Action<T> set;

    public ValueWrapper(Func<T> get, Action<T> set)
    {
        this.get = get;
        this.set = set;
    }

    public T Value
    {
        get
        {
            return get();
        }

        set
        {
            set(value);
        }
    }
}

Затем вы можете использовать это для изменения значений:

void run()
{
    int x = 0;

    var intWrapper = new ValueWrapper<int>(() => x, value => x = value);

    test(intWrapper);

    Console.WriteLine(x);  // Prints 42, which shows that x was changed.

    TextBox textBox = new TextBox {Text = ""};

    var stringWrapper = new ValueWrapper<string>(() => textBox.Text, value => textBox.Text = value);

    test(stringWrapper);

    Console.WriteLine(textBox.Text); // Prints "Changed".
}

static void test(ValueWrapper<int> wrapper)
{
    wrapper.Value = 42;
}

static void test(ValueWrapper<string> wrapper)
{
    wrapper.Value = "Changed";
}

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

void run()
{
    TextBox textBox = new TextBox {Text = ""};

    var wrapper = test1(textBox);
    test2(wrapper);
    Console.WriteLine(textBox.Text); // Prints "Changed"
}

void test2(ValueWrapper<string> wrapper)
{
    wrapper.Value = "Changed";
}

ValueWrapper<string> test1(TextBox textBox)
{
    return new ValueWrapper<string>(() => textBox.Text, value => textBox.Text = value);
}

Предупреждение: это приводит к довольно головокружительному коду, например:

void run()
{
    var intWrapper = test();
    intWrapper.Value = 42;
    Console.WriteLine(intWrapper.Value); // Works, but where is the value? It can't be the x inside test()!
}

ValueWrapper<int> test()
{
    int x = 0;
    var intWrapper = new ValueWrapper<int>(() => x, value => x = value);
    return intWrapper;
}

Таким образом, мы вернули ValueWrapper из test(), который, по-видимому, является оберткой для локальной переменной из test(). И тогда мы, по-видимому, можем изменить значение и распечатать его...

Это, конечно, не совсем то, что происходит, но это может сбивать с толку!

person Matthew Watson    schedule 19.06.2013
comment
+1, хотя мне любопытно, почему делегаты get и set (а не просто сохранение значения в переменной экземпляра) - person Adam Robinson; 19.06.2013
comment
@AdamRobinson Потому что я специально хотел изменить исходное значение, то есть значение x в стеке в методе test(). Обратите внимание, как на самом деле изменяется значение x, когда мы вызываем метод test(), из-за чего оно выглядит так, как будто ссылка на x каким-то образом использовалась внутри test(). Магия захваченных переменных. :) - person Matthew Watson; 19.06.2013
comment
Я знаю, что могут делать замыкания, но учитывая, что на самом деле (на самом деле) они делают то же самое (создают класс для размещения локальной переменной, а затем заменяют каждый доступ к локальной переменной после закрытия доступом к свойству на экземпляре этого класса), но без накладных расходов на вызов делегата я не вижу преимущества. На самом деле никакого волшебства нет :) - person Adam Robinson; 19.06.2013
comment
@AdamRobinson Я имею в виду, если вы посмотрите на строку Console.WriteLine(x);, она использует x напрямую, не оболочку значения. И вы можете видеть, что x был изменен. Я специально не заменяю каждый доступ доступом к свойству (под которым, я думаю, вы подразумеваете использование оболочки вместо исходного значения). Изменения, сделанные оболочкой, видны коду, который использует исходное значение, это важный момент. - person Matthew Watson; 19.06.2013
comment
В вашем коде да, но не в реальности. Взгляните на доступную документацию по замыканиям и захвату переменных в анонимных делегатах. Эти функции полностью синтаксический сахар; они на самом деле не расширяют область действия или видимость переменной экземпляра за пределами объявляющей функции (поскольку это невозможно). Или, еще лучше, посмотрите на ИЖ. Вы обнаружите, что IL делает именно то, что я описываю. - person Adam Robinson; 19.06.2013
comment
@AdamRobinson Хм, я действительно не понимаю, что ты говоришь. Но рассмотрим мой второй пример, в котором метод test1() возвращает оболочку значения, и я передаю ее методу test2(), который использует его для изменения свойства Text исходного объекта TextBox. Это то, что я имею в виду. - person Matthew Watson; 19.06.2013
comment
Я имею в виду, что концепция замыканий — это особенность языка C#, а не среды выполнения .NET. На самом деле локальная переменная никогда и никогда не может быть представлена ​​за пределами функции, которая ее объявляет. Outside включает даже анонимных делегатов, которые объявлены внутри функции, содержащей локальный. Когда вы закрываете локальную переменную, она повышается до переменной экземпляра в анонимном классе, и в скомпилированном коде все ссылки на локальную переменную, которые имеют место после ее закрытия, являются ссылками на локальный экземпляр этого анонимного класса. - person Adam Robinson; 19.06.2013
comment
В результате то, что вы получите, используя описанный выше подход, по существу идентично простому подходу-оболочке, который я использовал в своем ответе, но у вас есть дополнительные накладные расходы на использование двух классов-оболочек и вызов простого получить и установить операции с помощью делегатов. Замыкания — отличный инструмент, и они определенно имеют свое место, но важно знать, что на самом деле происходит, когда вы используете языковые функции, подобные этой. - person Adam Robinson; 19.06.2013
comment
@AdamRobinson Я до сих пор не уверен, как вы могли бы использовать свой подход для изменения типа значения x, как в моем первом примере, а затем наблюдать за измененным значением, проверяя x, а не что-либо еще. Ваш подход работает только со ссылочными типами? (Я пытался ответить на упоминание ОП об использовании ints.) В любом случае, я уверен, что это не то, что должен делать ОП - они должны изменить дизайн! - person Matthew Watson; 19.06.2013
comment
Нет. Это ничем не отличается, поскольку тип оболочки по-прежнему является ссылочным типом независимо от значения, которое он содержит. - person Adam Robinson; 19.06.2013

в этом случае вы можете использовать указатели, использовать небезопасное ключевое слово для метода и установить небезопасный проект, чтобы разрешить указатели в С#, также вы можете инкапсулировать значение в классе, а в С# каждый класс имеет ссылочный тип

person ahmedsafan86    schedule 19.06.2013

я использовал это и отлично работает:

эксп.

public int value1 = 3;
public int value2 = 4;
public int value3 = 5;

public void Method1()
{
    int[] values = { value1, value2, value3};
    for (int i = 0; i < values.Length; i ++)
    {
        Console.WriteLine(values[i]);
    }
}
person lou34964    schedule 09.08.2018
comment
Объекты формы, такие как Labels, также могут это делать. - person lou34964; 09.08.2018