11

I'm coming from a C++ background. This question has been asked before, but try as I might I cannot find the answer. Let's say I have:

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

Can I create a string that references the above (neither of these will compile):

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

I do understand that strings are immutable in C#. I also understand that you cannot get the address of a managed object.

I'd like to do this:

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

I have read the Stack Overflow question Make a reference to another string in C# which has an excellent answer, but to a slightly different question. I do NOT want to pass this parameter to a function by reference. I know how to use the "ref" keyword for passing a parameter by reference.

If the answer is "You cannot do this in C#", is there a convenient workaround?

EDIT: Some of the answers indicate the question was unclear. Lets ask it in a different way. Say I needed to manipulate all items in the original long-named array that have prime indices. I'd like to add aliases to Array...[2], Array...[3], Array...[5], etc to a list. Then, modify the items in the list using a "for" loop (perhaps by passing the list just created to a function).

In C# the "using" keyword creates an alias to a class or namespace. It seems from the answers, that it is not possible to create an alias to a variable, however.

Community
  • 1
  • 1
AlainD
  • 3,501
  • 2
  • 25
  • 63
  • 1
    The answer unfortunately *is* "you cannot do this", but you could go `unsafe` and get access to pointers. – Jon May 20 '14 at 11:00
  • You could create a wrapper object that contains a string that you manipulate. – juharr May 20 '14 at 11:04
  • 8
    What's the context here? Why don't you want to use ArrayOfReallyVeryLongStringNames[439] directly? – qxg May 20 '14 at 11:08
  • there is no actual need for that, `string` is immutable and C# is garbage collected which allows `string` to share the underlying `char` array without issue. Literals are explicitly [interned](http://en.wikipedia.org/wiki/Interned_string) (aka the array is built for you) – ratchet freak May 20 '14 at 14:41
  • See also: [Why doesn't C# support the return of references?](http://stackoverflow.com/q/6339602/555045) It may not seem relevant, but the top answer is. – harold May 20 '14 at 15:27
  • 1
    Strings are reference types, which roughly correspond to pointers. So a simple `b = a` means that you now have two pointers to the same memory. So I don't get the point of your question. – CodesInChaos May 20 '14 at 16:35
  • 1
    @CodesInChaos OP wants a reference to the reference stored in the array, a pointer to pointer to string in C terms, if you prefer – Niklas B. May 20 '14 at 23:14
  • @NiklasB. The OP may be asking for that, but I don't think they actually *want* that. – CodesInChaos May 21 '14 at 07:51
  • @qxg: The context isn't relevant, is it? The question is clear enough. If you *really* need context, I would have liked to create an array of aliases to the strings I need to manipulate, store those aliases in an array, and then manipulate the array in a for-loop. – AlainD May 21 '14 at 16:15
  • @AlainD - Why not just store the strings in an `List` and manipulate it in a `foreach`-loop? That's the C# way to do it (if I understand what you want to do) – Bobson May 21 '14 at 18:54

8 Answers8

12

You could create a wrapper that keeps a reference to the underlying array AND the index of the string:

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

Then this will work:

    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"

You could make this more convenient to use by providing an implicit string operator so you don't have to use .Value to access the underlying string:

// Add this to class ArrayStringReference implementation

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

Then instead of having to access the underlying string like this:

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

You can do this:

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

This is just syntactic sugar, but it might make it easier to start using an ArrayStringReference in existing code. (Note that you will still need to use .Value to set the underlying string.)

Matthew Watson
  • 90,570
  • 7
  • 128
  • 228
5

The closest you can get is this:

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

Which gives an exception:

Cannot take the address of, get the size of, or declare a pointer to a managed type ('string')

So no, not possible...

Also read this MSDN article which explains what types can be used (blittable types).

Patrick Hofman
  • 143,714
  • 19
  • 222
  • 294
2

When I do something like this in C#:

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

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

The thing is, both "String 1" and "String 2" literals are created at the start of the program, and strings are always pointers: at first a references "String 1" literal and afterwards it references "String 2". If you want them to always reference the same thing, in C# you just use the same variable.

The string objects themselves are immutable in C#:

Because a string "modification" is actually a new string creation, you must use caution when you create references to strings. If you create a reference to a string, and then "modify" the original string, the reference will continue to point to the original object instead of the new object that was created when the string was modified.

When the string mutability is needed, for example, to concatenate a lot of strings faster, other classes are used, like StringBuilder.

To sum it up, what you're trying to do is impossible.

Max Yankov
  • 10,076
  • 10
  • 54
  • 116
2

In C#, a String is an Object. Therefore String a = "Donkey Kong" says that a now have a reference to this string that is being allocated over the memory. Then all you need to do is:

ArrayOfReallyVeryLongStringNames[439] = a;

And that will copy the refrence (which you should be thinking of in C#!!!) to the location in the string.

BUT!! When you do a="new string";, a will get a new reference. See the example I made: http://prntscr.com/3kw18v

You can only do this with unsafe mode.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
oz adari
  • 118
  • 8
1

You could create a wrapper

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";
juharr
  • 30,127
  • 4
  • 48
  • 88
1

What you are trying to do is universally discouraged, and actively prevented, in C#, where the logic should be independent of the memory model, however, refer to related SO question C# memory address and variable for some info.

EDIT 1

A more canonical approach to your actual problem in C# would be:

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

If you really want to modify the original memory structure itself, then perhaps C++ is a more appropriate language choice for the solution.

EDIT 2

Looking around on SO, you may want to look at this answer Hidden Features of C#? to an unrelated question.

Community
  • 1
  • 1
david.barkhuizen
  • 4,516
  • 4
  • 31
  • 34
  • Can't agree that this is "universally discouraged" or that it is "actively prevented". "using" and "ref" are two C# keywords used regularly. – AlainD May 21 '14 at 16:39
0
        [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";
    }
Rajnikant
  • 1,501
  • 13
  • 19
0

Of course you can, hehe:

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

But you would have to have a very good reason to do it this way.

IS4
  • 9,770
  • 2
  • 41
  • 69