144

I have read that .NET supports return of references, but C# doesn't. Is there a special reason? Why I can't do something like:

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 
Tomas Ramirez Sarduy
  • 17,294
  • 8
  • 69
  • 85

4 Answers4

190

This question was the subject of my blog on June 23rd 2011. Thanks for the great question!

The C# team is considering this for C# 7. See https://github.com/dotnet/roslyn/issues/5233 for details.

UPDATE: The feature made it in to C# 7!


You are correct; .NET does support methods that return managed references to variables. .NET also supports local variables that contain managed references to other variables. (Note however that .NET does not support fields or arrays that contain managed references to other variables because that overly complicates the garbage collection story. Also the "managed reference to variable" types are not convertible to object, and therefore may not be used as type arguments to generic types or methods.)

Commenter "RPM1984" for some reason asked for a citation for this fact. RPM1984 I encourage you to read the CLI specification Partition I Section 8.2.1.1, "Managed pointers and related types" for information about this feature of .NET.

It is entirely possible to create a version of C# which supports both these features. You could then do things like

static ref int Max(ref int x, ref int y) 
{ 
  if (x > y) 
    return ref x; 
  else 
    return ref y; 
} 

and then call it with

int a = 123;
int b = 456; 
ref int c = ref Max(ref a, ref b); 
c += 100;
Console.WriteLine(b); // 556!

I know empirically that it is possible to build a version of C# that supports these features because I have done so. Advanced programmers, particularly people porting unmanaged C++ code, often ask us for more C++-like ability to do things with references without having to get out the big hammer of actually using pointers and pinning memory all over the place. By using managed references you get these benefits without paying the cost of screwing up your garbage collection performance.

We have considered this feature, and actually implemented enough of it to show to other internal teams to get their feedback. However at this time based on our research we believe that the feature does not have broad enough appeal or compelling usage cases to make it into a real supported language feature. We have other higher priorities and a limited amount of time and effort available, so we're not going to do this feature any time soon.

Also, doing it properly would require some changes to the CLR. Right now the CLR treats ref-returning methods as legal but unverifiable because we do not have a detector that detects this situation:

ref int M1(ref int x)
{
    return ref x;
}

ref int M2()
{
    int y = 123;
    return ref M1(ref y); // Trouble!
}

int M3()
{
    ref int z = ref M2();
    return z;
}

M3 returns the contents of M2's local variable, but the lifetime of that variable has ended! It is possible to write a detector that determines uses of ref-returns that clearly do not violate stack safety. What we would do is write such a detector, and if the detector could not prove stack safety, then we would not allow the usage of ref returns in that part of the program. It is not a huge amount of dev work to do so, but it is a lot of burden on the testing teams to make sure that we've really got all the cases. It's just another thing that increases the cost of the feature to the point where right now the benefits do not outweigh the costs.

If you can describe for me why it is you want this feature, I would really appreciate that. The more information we have from real customers about why they want it, the more likely it will make it into the product someday. It's a cute little feature and I'd like to be able to get it to customers somehow if there is sufficient interest.

(See also related questions Is it Possible to Return a Reference to a Variable in C#? and Can I use a reference inside a C# function like C++?)

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • @EricLippert I read about the same in your blog some time ago.. could you give me the link to the post? Can't find it right now. – Oscar Mederos Jun 14 '11 at 15:57
  • @Oscar: I don't believe I've ever blogged about this proposed feature. I've mentioned it a couple of times here and there on StackOverflow; this isn't the first time it has come up here. I have blogged about other ways in which "reference to variable" types make things complicated; perhaps you are thinking of one of those? – Eric Lippert Jun 14 '11 at 15:59
  • @EricLippert Take a look at the code of the `Ref` class in this question: http://stackoverflow.com/questions/5013064/datagrid-additional-column-and-row Is there a place where you shared it? Can't remember it right now :( Maybe I'm just wrong ;) – Oscar Mederos Jun 14 '11 at 16:12
  • @Oscar: Are you maybe thinking of this article? http://blogs.msdn.com/b/ericlippert/archive/2011/03/07/references-and-pointers-part-one.aspx – Eric Lippert Jun 14 '11 at 16:19
  • @Oscar: Or you might be thinking of this one, where I mention the feature in passing: http://blogs.msdn.com/b/ericlippert/archive/2011/03/29/compound-assignment-part-one.aspx – Eric Lippert Jun 14 '11 at 16:42
  • @EricLippert No, those definitely aren't :( Maybe I'm just confussed. If I find it I'll let you know. – Oscar Mederos Jun 14 '11 at 17:11
  • 4
    @EricLippert: I have no convincing example is just something I was wondering. Excellent and compelling response – Tomas Ramirez Sarduy Jun 14 '11 at 20:43
  • @Oscar: Do you mean [this](http://stackoverflow.com/questions/4542536/is-it-possible-to-return-a-reference-to-a-variable-in-c/4543089#4543089) (and then see also [this](http://stackoverflow.com/questions/3284767/can-i-use-a-reference-inside-a-c-function-like-c/3284878#3284878) ). – Brian Jun 15 '11 at 14:01
  • @Brian Exactly! @EricLippert, take a look at http://stackoverflow.com/questions/4542536/is-it-possible-to-return-a-reference-to-a-variable-in-c/4543089#4543089 :) That's what I was talking about. In fact, I upvoted you when I read it first time – Oscar Mederos Jun 16 '11 at 01:54
  • 1
    @Eric: In your excample, wouldn't be more suitable to keep `y` _alive_ after returning from `M2`? I would expect this feature to work like lambdas capturing locals. Or is the behavior you proposed because it is how CLR handles that scenario? – Fede Jun 23 '11 at 15:16
  • I see the power of this, but I feel like it would make debugging harder, particularly with references to value types. Intuitively, you expect the only way for an `int` to change is by touching it directly: `a = 2`, `a++`, etc. Being able to change a completely different variable and have the changes ripple through other variables seems frightening (I realize you could put side-effects in a setter, but as I understand it, that's to be avoided). I also prefer to be able to click "show all references" in the IDE and be confident I can trace through them all. – Justin Morgan - On strike Jun 24 '11 at 17:19
  • I'm not saying it *breaks* debugging per se, just that it seems to introduce more complexity than it resolves, and IMHO it changes the feel and intuitiveness of C# somewhat. – Justin Morgan - On strike Jun 24 '11 at 17:19
  • @Justin Morgan: I would argue that such a feature would allow one to actually use value types in places where one would like to use them but presently cannot; this would make debugging easier. If Foo and Bar are value types and I say "Foo=Bar; Foo.X=5;" I'll have a Foo that's like Bar, except that X=5. I haven't changed anything else. If I had e.g. a Dictionary it would be useful to be able to say MyDict("Foo").X=5 and know that I wasn't changing MyDict("Bar"). – supercat Jun 30 '11 at 23:48
  • 3
    @Eric: IMHO the ability to have properties return references to value types is a major omission in the .net languages. If Arr is an Array of a value type (e.g. Point), one can say e.g. Arr(3).X=9 and know that one hasn't changed the value of Arr(9).X or even SomeOtherArray(2).X; if Arr were instead an array of some reference type, no such guarantees would exist. The fact that an array's indexing operator returns a reference is extremely useful; I consider it highly unfortunate that no other type of collection can provide such functionality. – supercat Jun 30 '11 at 23:55
  • @Eric: I posted a comment to your blog alluding to an alternative mechanism for reference properties; the latter approach would require that the compiler be able to rewrite code, and to make it work in the general case would require a means of having variadic generic functions and delegates [e.g. SomeFunc<...>(int, ActionByRef,...); to expand to SomeFunc(int, ActByRef, ref T, ref U, ref V);] If compilers could handle it, the latter approach could in some ways be better than 'simple' references, since an object could expose an... – supercat Jul 01 '11 at 00:02
  • ...expandable delegate to allow others to reference a field directly (e.g. with Interlocked.CompareExchange), and yet be notified in advance when a reference was being created and when it was no longer needed. For example, one could have an ObservableReferenceDictionary(String, Point) which could fire notification events when someone said MyDict("George").X=9 (one could receive notification both before and after the access occurs). Unless the language or CLR enforced the pattern "DoSomeStuff; CallTheDelegate(params...); DoMoreStuff;" things could get confusing, though. – supercat Jul 01 '11 at 00:09
  • 4
    Eric, what is the best way to provide the feedback regarding scenarios (as you suggest in the last paragraph) to maximize the chances that it's seen? MS Connect? UserVoice (with its arbitrary 10 post/vote limit)? Something else? – Roman Starkov Aug 08 '12 at 08:51
  • Returning a reference to an item in a list from a method is something that is frequently needed. The problem is that `MyClass MyObject=FindMyObject(String Key);` will be assigned a reference to the object. If I want to use MyObject to _change the value of the item in list_ though, I can't, because `MyObject=new MyClass();` will just assign the new reference to MyObject not the item that was returned from the function. So, if `ref MyClass FindMyObject(String Key)` was allowed, I could, then, use MyObject to reassign directly the item I need, instead of having to return an index that would – ThunderGr Dec 20 '12 at 12:01
  • use to call another method to do the reassignment. (Something like `int theIndex=FindMyObject(String Key); AssignMyObject(theIndex, new MyClass();`) – ThunderGr Dec 20 '12 at 12:03
  • And, another point here. C++ does not care if you screw up with returning references to local variables. The point is, "I give you what you need to do your job, but you are responsible if you mess up". So, you do not, actually, have to write a detector for the stupidity of a developer to return a reference to a local variable. Anyone using advanced features like that should know how to handle it. – ThunderGr Dec 20 '12 at 12:14
  • 1
    @ThunderGr: That is the C# philosophy for "unsafe" -- if you write code that is possibly memory-unsafe then C# insists that you mark it as "unsafe", so that you are taking responsibility for memory safety. C# already has the unsafe version of the feature, provided that the variable in question is of unmanaged type. The question is whether the C# team should make a unsafe version that handles managed types. If doing so makes it easy for the developer to write terrible bugs, it's not going to happen. C# is not C++, a language which makes it easy to write terrible bugs. C# is safe by design. – Eric Lippert Dec 20 '12 at 14:55
  • To try and protect developers from mistakes in one thing. To try to protect them against stupidity is another. A programming language can be as safe as safe can be. If you were to protect people from themselves, you would have to lock them up and put them in a straitjacket. Too much safety is more harmful than none. – ThunderGr Dec 21 '12 at 14:18
  • In theory, would it be possible for `List::operator[]` to return by reference, so we could say `myStructList[0].someMember = newValue`? Or does the CLR not support references into arrays? – fredoverflow May 02 '13 at 20:15
  • @FredOverflow: Yes that would be in theory possible, but handing out refs to private internal data structures is dangerous. For example, a contrived case: `ref int I(List list) { try { return ref list[0]; } finally { list.Clear(); } } ... I(myList[0]).someMember = newValue;` ? You take a ref to the interior of a data structure that then immediately becomes garbage! What if the array is put *back into a pool* when the list is cleared? Now we've modified memory that might belong to *some other data structure* that has fetched it out of the pool. – Eric Lippert May 02 '13 at 20:55
  • 1
    @EricLippert: Your last comment is interesting. If `List` included a call `ActOnItem(int index, ActByRef proc)` which then invoked `proc(ref list[index])`, I expect the GC would ensure that `list` stays around until the procedure call returns. In your scenario, would the byref keep `list` around as long as it existed? If so, by what mechanism? If it wouldn't, I would think a returned byref was basically an `unsafe` pointer. – supercat Sep 18 '13 at 20:51
  • @supercat: The mechanism of such a ref is basically a safe pointer. If the variable being ref'd is on the heap then the GC will keep it around at least until the ref goes away. That however can be earlier than you might expect, if it is enregistered, and the jitter knows that it is safe to use that register for something else. (Because say it knows that the ref is never read from or written to again.) – Eric Lippert Sep 18 '13 at 21:48
  • @EricLippert: In x86, if an object has twenty `int` fields, at some moment in time the first is at address 0x12340000, and a function returns a byref to the second, would the byref simply hold 0x12340004, and the GC would have to recognize that that address is within the range of addresses occupied by the object, or would there be more to it than that? Could the GC move the object so its first field is at 0x23450000 if it updates the byref to 0x23450004, or would the object be pinned while the ref exists? – supercat Sep 18 '13 at 23:01
  • If I understood it correctly I will not be able to declare a local variable in a function (which gets allocated on stack) and return its reference back to the caller? Or I will be able to do it for reference types at least whose object memory allocation happens on heap? Currently I can initialize a ref local variable only to a parameter which has been received by the function by reference. Can you please confirm this behavior for the upcoming C# v7.0 release. Honestly being a managed code developer since my birth I'm unable to comprehend a real use case where I would put this feature to use. – RBT Nov 11 '16 at 01:10
  • 1
    @RBT: When I was working on the prototype we had a proposal to make it verifiable, safe and cheap, but with restrictions to ensure that only refs to variables known to be alive could be returned. There are some scenarios in which stack-allocated vars can be safely returned; some of them would work. I don't know the details of the upcoming release; I'm behind on my reading. The notes are public; check them out. The use cases are pretty obscure and mostly involve construction of high-perf low level mechanisms rather than line of business code. – Eric Lippert Nov 11 '16 at 02:46
21

You are talking about methods that return a reference to a value type. The only built-in example in C# that I know of is the array-accessor of a value type:

public struct Point
{
    public int X { get; set; }
    public int Y { get; set; }
}

and now create an array of that struct:

var points = new Point[10];
points[0].X = 1;
points[0].Y = 2;

In this case points[0], the array indexer, is returning a reference to struct. It is impossible to write your own indexer (for example for a custom collection), that has this same "return a reference" behavior.

I didn't design the C# language so I don't know all the reasoning behind not supporting it, but I think that the short answer might be: we can get along just fine without it.

Rick Sladkey
  • 33,988
  • 6
  • 71
  • 95
  • 8
    Tom is asking about methods that return a reference *to a variable*. The variable need not be of value type, though of course that is usually what people want when they want ref-returning methods. Otherwise, great analysis; you are correct that the only place in the C# language where a complex expression produces a *ref* to a variable that the user can then manipulate is the array indexer. (And of course the member access operator "." between a receiver and a field, but that's pretty obviously an access to a variable.) – Eric Lippert Jun 14 '11 at 15:32
1

You could always do something like:

public delegate void MyByRefConsumer<T>(ref T val);

public void DoSomethingWithValueType(MyByRefConsumer<int> c)
{
        int x = 2;
        c(ref x);
        //Handle potentially changed x...
}
Eladian
  • 958
  • 10
  • 29
1

C# 7.0 has support for returning references. See my answer here.

Community
  • 1
  • 1
CodingYoshi
  • 25,467
  • 4
  • 62
  • 64