0

I found some code in our app that passes a List<T> by reference to indicate it is modified:

void DoSomething(ref List<MyType> theList)
{
    theList.Add(new MyType());
}

I think it is clear that the ref-keyword is obsolete in this case, as we could also add new elements to the list without the keyword. However it indicates that we modify the lists or at least its elements. I find this is particulary useful if you have many parameters and want to see which of them are modified and which are just passed as values to do the job.

The question if this is okay is surely opinion-based and would be invalid for SO, I rather ask if there´s another approach to achieve this or if I even should care about it.

EDIT: To clarify my question a bit. This question is not on if a list is modified, this was just an example. Alternatively I´d also use any other reference-type, not just a List<T>.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • Are you looking for an equivalent to C++ `const` for parameters? – Bernhard Hiller Jan 23 '17 at 08:47
  • @BernhardHiller I doubt so, I want to indicate a parameter is *modified* in my method, not the opposite. – MakePeaceGreatAgain Jan 23 '17 at 08:47
  • 1
    In C# there is no equivalent for C++ const modifiers. This effect achieves through interface implementations and restrictions inside wrappers. – eocron Jan 23 '17 at 08:49
  • Using keywords that have an *actual function* as vague documentation (for something that is even unrelated to the actual keyword) seems outright crazy to me - I'd probably advise that if you need to denote in your parameter list which parameters are "changed" that you just need to do some heavy refactoring to make your code easier to follow. Start by taking out `ref` keywords that you don't need. – Ant P Jan 23 '17 at 08:51
  • 1
    The correct way to do this is use `IReadOnlyList` (or `ImmutableList`) parameters to indicate when a list will *not* be modified, and assume that the contents of any `List` parameters WILL be modified. Of course, this doesn't work well with legacy code, but it's still the correct approach. – Matthew Watson Jan 23 '17 at 09:05
  • @MatthewWatson See my edit. – MakePeaceGreatAgain Jan 23 '17 at 09:13

3 Answers3

5

However it indicates that we're changing the list.

No, it does not. It indicates the reference can be changed. Using a ref keyword as 'identifier' something can change is bad in my opinion since it opens doors you might not want.

I would advice to look into aspect-oriented programming, were you can assign attributes to method parameters. From the new Roslyn compiler with its code analysis services you could even check if the code violates the principal given.

Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
0

Patrick lead me to an alternative way that doesn't rely on attributes so we can use it in legacy-code on machines with an older compiler. We can of course write the API-doc appropriately to indicate that the method modifies the passed parameter:

/// <summary/>
/// <param name="theList">list to be modified</param>
void DoSomething(List<MyType> theList)

I guess this is better as it doesn't rely on a wrong usage of the keyword. However it assumes clients of our API read the docs carefully.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
-2

It is much easier and broad to read interface than see ref and consider it changable collection.

For example, if you want it to be constant:

public DoSomething<T>(IReadOnlyCollection<T> collection)
{
     //....
}

Non-constant:

public DoSomething<T>(List<T> collection)
{
     //....
}

In case of value type it is indeed as you clarify in your question:

Constant:

public DoSomething(int value)
{
     //....
}

Non-constant:

public DoSomething(ref int value)
{
     //....
}

Or, if you have type without access to code, for example, Stream:

public class StreamWrapper
{
    private Stream _instance;

    //now you can specify read or edit methods here and use this class in invokation.
}
eocron
  • 6,885
  • 1
  • 21
  • 50