0

Currently I have a list containing structs, and I use a delegate to find a struct in the list as such:

stTest item = myList.Find(delegate(stTest test) 
{
   return ( string.Compare(test.StringA, somestring, true) == 0 );
});

This has been working perfectly for getting an item, but it seems that it returns a copy of the item it found and not the actual item, so if I modify fields in the item (which I do), the updates don't take effect on the item in the list.

Is there any way to get the delegate to return a reference to the item in the list?

riqitang
  • 3,241
  • 4
  • 37
  • 47

3 Answers3

2

If you are trying to mutate the object in-place, you can "fake it" with a slightly nicer syntax by employing an extension method like this:

public delegate void Mutator<T>(ref T arg) where T : struct;

public static void FindAndMutate<T>(
    this IList<T> self,
    Predicate<T> predicate,
    Mutator<T> mutator) where T : struct
{
    if (self == null) { throw new ArgumentNullException("self"); }
    if (predicate == null) { throw new ArgumentNullException("predicate"); }
    if (mutator == null) { throw new ArgumentNullException("mutator"); }

    for (int i = 0; i < self.Count; ++i) {
        var value = self[i];

        if (predicate(value)) {
            mutator(ref value);

            self[i] = value;
        }
    }
}

The usage would look something like this:

struct AStruct
{
    public string String;
    public int Int;
}

var list = new[] {
    new AStruct() { String = "hello", Int = 0},
    new AStruct() { String = "world", Int = 1}
};

list.FindAndMutate(i => i.Int == 0, (ref AStruct i) => i.String = "goodbye");

This is obviously not a perfect solution, and does involve copying of the struct inside of the extension method.

Really, if you need reference semantics you should be using a reference type instead of a value type.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
  • Nice example, but the better approach (as others have mentioned) is to just re-factor the struct (actually it was a few tightly-coupled structs) into a class. – riqitang Mar 21 '13 at 16:16
  • @Sean Hence the last line of my answer. This is a kludge that should only be used if there isn't another good option (for example, if the struct in question lives in a different codebase, or if the containing assembly is already public and therefore changing the struct into a class would break ABI). – cdhowie Mar 21 '13 at 16:18
0
item=myList.FirstOrDefault(entry=> 
     string.Compare(entry.StringA, someString, true)==0);

Note that struct is not a reference type, you cannot do that, unless you want to box it into a reference type, but that will bring additional overhead.

If you really want to use reference, consider to repalce your struct with class.

David
  • 15,894
  • 22
  • 55
  • 66
  • This does not answer the question. It will not return a reference. – cdhowie Mar 21 '13 at 15:06
  • it does, how do you know it is not a reference? C# by default is by reference, instead of by value. – David Mar 21 '13 at 15:08
  • 1
    Because the OP clearly said *"a list containing structs."* A struct is not a reference type. – cdhowie Mar 21 '13 at 15:08
  • @David C# by default always passes parameters by value. Parameters are only passed by reference when the `ref` or `out` parameters are used. – Servy Mar 21 '13 at 15:14
  • yes, but there is nothing related with argument. All is within the same function, where did you find this is passed to a function? Say A=SomeInstance; B=A; B is just another reference to SomeInstance. 3x – David Mar 21 '13 at 15:16
0

It will work if you change the list members from struct to class, because structs are always copied.

David S.
  • 5,965
  • 2
  • 40
  • 77
  • Well, structs aren't *always* copied, you'd just need to modify the definition of `List` to have the method pass the parameter by reference. – Servy Mar 21 '13 at 15:24
  • If you mean the definition of the delegate, I agree on that. – David S. Mar 21 '13 at 15:27