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

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

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

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

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

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

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

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

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

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

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

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

8 ответов


ближайший вы можете получить это:

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

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

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

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

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


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

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 to set базовый строку.)


когда я делаю что-то подобное в C#:

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". Если вы хотите, чтобы они всегда ссылались на одно и то же, в C# вы просто используете одну и ту же переменную.

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

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

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

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


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

ArrayOfReallyVeryLongStringNames[439] = a;

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

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

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


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

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

то, что вы пытаетесь сделать, повсеместно обескуражено и активно предотвращается в C#, Где логика должна быть независимой от модели памяти, однако, обратитесь к связанному SO question C# адрес памяти и переменная для некоторых информация.

изменить 1

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

        // 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
        */

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

Изменить 2

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


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

конечно можно, хехе:

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

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