Получить строку для ссылки на другую в С#

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

string[] ArrayOfReallyVeryLongStringNames = new string[500];
ArrayOfReallyVeryLongStringNames[439] = "Hello world!";

Могу ли я создать строку, которая ссылается на выше (ни одна из них не будет скомпилирована):

string a = ref ArrayOfReallyVeryLongStringNames[439];  // no compile
string a = &ArrayOfReallyVeryLongStringNames[439];     // no compile

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

Я хотел бы сделать это:

a = "Donkey Kong";  // Now ArrayOfReallyVeryLongStringNames[439] = "Donkey Kong";

Я прочитал вопрос о переполнении стека Сделать ссылку на другую строку на С# который имеет отличный ответ, но к немного другому вопросу. Я НЕ хочу передать этот параметр функции по ссылке. Я знаю, как использовать ключевое слово "ref" для передачи параметра по ссылке.

Если ответ "Вы не можете сделать это в С#", есть ли удобный способ обхода?

EDIT: Некоторые из ответов указывают на то, что вопрос неясен. Давайте попробуем об этом по-другому. Скажем, мне нужно было манипулировать всеми элементами в исходном длинноименованном массиве с основными индексами. Я хотел бы добавить псевдонимы в Array... [2], Array... [3], Array... [5] и т.д. В список. Затем измените элементы в списке, используя цикл "for" (возможно, передав список, только что созданный для функции).

В С# ключевое слово "using" создает псевдоним для класса или пространства имен. Как видно из ответов, невозможно создать псевдоним для переменной.

Ответ 1

Ближе всего вы можете получить следующее:

unsafe
{
    string* a = &ArrayOfReallyVeryLongStringNames[439];     // no compile
}

Что дает исключение:

Невозможно принять адрес, получить размер или объявить указатель на управляемый тип ('string')

Так что нет, невозможно...

Также читайте эту статью статью MSDN, в которой объясняется, какие типы могут использоваться (типы blittable).

Ответ 2

Вы можете создать оболочку, содержащую ссылку на базовый массив И индекс строки:

public sealed class ArrayStringReference
{
    private readonly string[] _array;
    private readonly int      _index;

    public ArrayStringReference(string[] array, int index)
    {
        _array = array;
        _index = index;
    }

    public string Value
    {
        get
        {
            return _array[_index];
        }

        set
        {
            _array[_index] = value;
        }
    }

    public override string ToString()
    {
        return Value;
    }
}

Тогда это будет работать:

    string[] ArrayOfReallyVeryLongStringNames = new string[500];
    ArrayOfReallyVeryLongStringNames[439] = "Hello world!";

    var strRef = new ArrayStringReference(ArrayOfReallyVeryLongStringNames, 439);
    Console.WriteLine(ArrayOfReallyVeryLongStringNames[439]); // Outputs "Hello world!"
    strRef.Value = "Donkey Kong";
    Console.WriteLine(ArrayOfReallyVeryLongStringNames[439]); // Outputs "Donkey Kong"

Вы можете сделать это более удобным для использования, предоставляя неявный оператор строки, поэтому вам не нужно использовать .Value для доступа к базовой строке:

// Add this to class ArrayStringReference implementation

public static implicit operator string(ArrayStringReference strRef)
{
    return strRef.Value;
}

Затем вместо доступа к базовой строке, как это:

strRef.Value = "Donkey Kong";
...
string someString = strRef.Value;

Вы можете сделать это:

strRef.Value = "Donkey Kong";
...
string someString = strRef; // Don't need .Value

Это просто синтаксический сахар, но это может облегчить начало использования ArrayStringReference в существующем коде. (Обратите внимание, что вам все равно нужно использовать .Value для установки базовой строки.)

Ответ 3

Когда я делаю что-то подобное на С#:

string a = "String 1";
string b = a;
a = "String 2";

Console.WriteLine(a); // String 2
Console.WriteLine(b); // String 1

Дело в том, что в начале программы создаются как литералы "String 1", так и "String 2", а строки всегда являются указателями: сначала ссылки "String 1", а затем ссылаются на "String 2", Если вы хотите, чтобы они всегда ссылались на одно и то же, на С# вы просто используете одну и ту же переменную.

Строковые объекты сами по себе неизменяемы в С#:

Поскольку строчная "модификация" на самом деле является новой строкой, вы должны соблюдать осторожность при создании ссылок на строки. Если вы создадите ссылку на строку и затем "измените" исходную строку, ссылка будет продолжать указывать на исходный объект вместо нового объекта, который был создан при изменении строки.

Когда необходима изменчивость строки, например, чтобы быстрее конкатенировать множество строк, используются другие классы, такие как StringBuilder.

Подводя итог, то, что вы пытаетесь сделать, невозможно.

Ответ 4

В С# строка представляет собой объект. Поэтому String a = "Donkey Kong" говорит, что a теперь имеет ссылку на эту строку, которая распределяется по памяти. Тогда все, что вам нужно сделать, это:

ArrayOfReallyVeryLongStringNames[439] = a;

И это скопирует refrence (который вы должны думать о С#!!!) в местоположение в строке.

НО!! Когда вы выполните a="new string";, a получит новую ссылку. См. Пример, который я сделал: http://prntscr.com/3kw18v

Вы можете сделать это только в небезопасном режиме.

Ответ 5

Вы можете создать обертку

public class StringWrapper
{
    public string Value {get;set;}
}

StringWrapper[] arrayOfWrappers = new StringWrapper[500];
arrayOfWrappers[439] = new StringWrapper { Value = "Hello World" };
StringWrapper a = arrayOfWrappers[439];
a.Value = "New Value";

Ответ 6

То, что вы пытаетесь сделать, повсеместно обескуражено и активно предотвращено, в С#, где логика должна быть независимой от модели памяти, однако, ссылайтесь на связанную SO-память С# адрес и переменная для некоторой информации.

ИЗМЕНИТЬ 1

Более канонический подход к вашей реальной проблеме в С# будет:

        // using System.Linq;

        string[] raw = new string[] { "alpha", "beta", "gamma", "delta" };

        List<int> evenIndices = Enumerable.Range(0, raw.Length)
            .Where(x => x % 2 == 0)
            .ToList();

        foreach (int x in evenIndices)
            raw[x] = raw[x] + " (even)";

        foreach (string x in raw)
            Console.WriteLine(x);

        /*
        OUTPUT: 

        alpha (even)
        beta
        gamma (even)
        delta
        */

Если вы действительно хотите изменить исходную структуру памяти, возможно, С++ является более подходящим выбором языка для решения.

ИЗМЕНИТЬ 2

Оглядываясь на SO, вы можете посмотреть этот ответ Скрытые функции С#? на не связанный с ним вопрос.

Ответ 7

        [TestMethod]
    public void TestMethod1()
    {
        string[] arrayOfString = new string[500];
        arrayOfString[499] = "Four Ninty Nine";
        Console.WriteLine("Before Modification : {0} " , arrayOfString[499]);

        string a = arrayOfString[499];

        ModifyString(out arrayOfString[499]);

        Console.WriteLine("after a : {0}", a);
        Console.WriteLine("after arrayOfString [499]: {0}", arrayOfString[499]);

    }

    private void ModifyString(out string arrayItem)
    {
        arrayItem = "Five Hundred less one";
    }

Ответ 8

Конечно, вы можете, хе-хе:

var a = __makeref(array[666]);
__refvalue(a, string) = "hello";

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