Impossible to use ref and out for first ("this") parameter in Extension methods?
Asked Answered
A

6

41

Why is it forbidden to call Extension Method with ref modifier?

This one is possible:

public static void Change(ref TestClass testClass, TestClass testClass2)
{
    testClass = testClass2;
}

And this one not:

public static void ChangeWithExtensionMethod(this ref TestClass testClass, TestClass testClass2)
{
    testClass = testClass2;
}

But why?

Afford answered 11/4, 2010 at 20:31 Comment(3)
Are you sure that an explicit ref is necessary? I would expect that it is "auto-generated" by this - non-reference extension methods wouldn't make any sense.Gogh
But they're non-reference if I'm not mistaken.Afford
@MarcelJackwerth ref parameters are not the same as reference type parameters. A ref parameter passes the caller's reference (or pointer) itself. With ref you can update the reference to point to some other object. Without it (for reference types) you can update the object itself, but not the reference to it.Nguyetni
I
25

You have to specify ref and out explicitly. How would you do this with an extension method? Moreover, would you really want to?

TestClass x = new TestClass();
(ref x).ChangeWithExtensionMethod(otherTestClass);
// And now x has changed?

Or would you want to not have to specify the ref part, just for the first parameter in extension methods?

It just sounds weird to me, to be honest, and a recipe for unreadable (or at least hard-to-predict) code.

Inquiline answered 11/4, 2010 at 20:41 Comment(16)
But that reason no longer applies for C# 4. In fact, in VB you can already use ByRef extension methods, if I remember correctly.Abubekr
"You have to specify ... explicitly" is an artificial compiler requirement that could be dropped here. That would make it even more obscure though.Violetteviolin
@Konrad: It only doesn't apply in C# 4 for COM methods. @Henk: Yes, it's 'artificial' - but for the sake of readability. I think it's an entirely reasonable restriction.Inquiline
Many static methods have almost nothing to do with the class type where they reside, and everything to do with the type of their first argument or constraints placed thereon. That is if anything more true of methods which take their first argument by ref than those which take it by value. In the line DisposeHelpers.DisposeAndNullOut(ref field1), the easiest item to spot--the static class name--is the least meaningful. The field name is probably the most important part, so why not give it more prominence? Perhaps field1.:DisposeAndNullOut()?Schwann
@supercat: Given that I try to avoid ref wherever possible, I really haven't found this being a problem. ref is pretty easy to spot in VS...Inquiline
@JonSkeet: I dislike the pattern foo = foo.WithSomeChange(). It cannot be made thread-safe, and it is awkward (sometimes dangerous) if foo is anything other than a simple variable. Passing foo as a ref parameter to a MakeSomeChange method allows it to be thread-safe, makes it clear that the method is going to change foo, and will cause the compiler will forbid dangerous uses. All big wins in my book. It would be nice to be able to keep those wins with a syntax that highlights the thing being acted upon, rather than the name of a static utility class.Schwann
@supercat: Yet again, I think we'll have to agree to disagree. I don't think it's worth going into any of this. (Do you go through my ancient posts looking for things to disagree with, out of interest?)Inquiline
@Jon Skeet - this is a very valid question for those of us who are coming to C# from VB. Just because you don't happen to agree does NOT mean that it's not worth discussing. Dismissing other people's opinions like that seems a bit elitist to me.Cerium
@Schwann - I also dislike the foo = foo.WithSomeChanges pattern. Using ByRef extension methods produces far more elegant code in my opinion, allow method calls & not just function calls. I also use them to be able to push the check for Nothing (null) into an extension method, & therefore not have to write it over & over in the calling code. I for one am glad you asked the question, as I just ran into the same problem. I'm trying to move to C#, but issues like this are very frustrating.Cerium
@YannDuran: I really wish that there had been a defined syntax for different styles of member invocation (akin to the C distinction between "." and "->" operators). Among other things, if readonlystruct.methodOrProperty had never been legal but the compiler had required something like (readonlystruct).methodOrProperty [to expressly indicate the creation of a temp copy] that would have nipped in the bud the justifiable complaints about "mutable struct" behavior]. I also would have liked something like foo.=WithX(5); for foo=foo.WithX(5);.Schwann
@supercat: I think it's fair to say that a language built with immutability strongly in mind would have more than a few differences to C#.Inquiline
@YannDuran: Where exactly did I dismiss other people's opinions? I expressed my opinion that it's a bit weird, and agreed to disagree with supercat. (Note that comments are explicitly not meant to turn into huge discussion threads on SO, and supercat and I have disagreed on a number of things like this before. It's rarely particularly constructive in the end. It's always polite, but I don't think anyone ever changes their mind ;) It would help if you could be clear exactly what you're objecting to.Inquiline
@Jon Skeet: I was referring to "I don't think it's worth going into any of this". I do find it worth it. And while I didn't mention it I find "Do you go through my ancient posts looking for things to disagree with, out of interest?", however politely phrased, to be unnecessary. I don't know you, & am not aware of any history you may have with supercat, but that doesn't change the fact that the question was of real value to a person coming from VB. VB is a very practical language, & it can be a shock to see C# purists argue for concepts that just make code more dogmatic than practical.Cerium
@YannDuran: As I said before, comment threads on SO are not meant to turn into discussions. That's not what SO is about, and this comment thread is already too long. My question to supercat was tongue-in-cheek, and I doubt that he took offence - we've had friendly disagreements on lots of topics. The question itself has been answered - further discussion about the merits of it belongs elsewhere, IMO.Inquiline
And as of C# 7.2, this ref extension methods for value types are allowed.Neurotic
While I agree extensions methods ref this should be avoided, you are not always in control of the classes you use, exemple abound, frameworks, API provided by 3rd parties etc. And there are valid cases where you have to actually create a new objects just to change its name for example (in my case it is when using Navisworks API). Best would be for the compiler to issue a warning for this so we can still do it knowing the consequences (and disable the warning ;->)Confiture
P
24

I know it's an old questions. But things have changed. In case anybody is looking for this.

Beginning with C# 7.2, you can add the ref modifier to the first argument of an extension method. Adding the ref modifier means the first argument is passed by reference. This enables you to write extension methods that change the state of the struct being extended.

This is allowed only for value types (struct), and not reference types (class, interface, record).

Source: Microsoft Docs, "Extension Methods (C# Programming Guide) — Extending Predefined Types".

public struct MyProperties
{
    public string MyValue { get; set; }
}

public static class MyExtensions
{
    public static void ChangeMyValue(this ref MyProperties myProperties)
    {
        myProperties.MyValue = "hello from MyExtensions";
    }
}

public class MyClass
{
    public MyClass()
    {
        MyProperties myProperties = new MyProperties();
        myProperties.MyValue = "hello world";
        myProperties.ChangeMyValue();
    }
}
Peerage answered 31/8, 2020 at 8:18 Comment(2)
IS there any way of doing this with a string since it is somewhere between a class and a value-type object? The compiler seems to treat it as a class and prevent it but unlike most classes, there is no way to modify its content, which is more like a value-type. This would open the possiblity for instance to set a default value if and only if the original string is null or empty. If it is not the case, no assignement is done at all.Chickie
@Chickie A string is not "in between" a struct and a class, it is simply an immutable class. Using ref for it would be pointless since it is already passed by reference.Suffumigate
H
16

In C# 7.2 you can use ref extension methods for structs

See https://github.com/dotnet/csharplang/issues/186 and https://github.com/dotnet/csharplang/blob/master/proposals/csharp-7.2/readonly-ref.md

Hamm answered 21/11, 2018 at 14:4 Comment(0)
A
14

I agree with the answers from Jon Skeet et al. about how allowing "ref this" extension methods could make the code more obscure. But if you look at some namespaces in the .Net Framework, it is common for a method invoked on a struct to alter it.

Take for example the System.Drawing structs (Point, Rectangle, etc). Each of these has methods (e.g. Offset, Inflate, etc) that mutate the struct itself. I'm not saying this is a good idea, in fact I personally find it very annoying that Offset, Inflate, etc mutate the structs themselves instead of returning new ones, and I know some of you are opposed to the idea of mutable structs in general.

I doubt there are any cases where invoking a method of a reference type will change the reference (unless it's with the String class, where I can imagine there might be some compiler magic to switch references to perform interning, etc). So it makes sense to prevent "this ref" from being used with reference types, because changing a reference would be a completely non-standard side-effect of calling a method.

But in regards to structs, allowing "this ref" would not significantly decrease code readability any more than Rectangle.Inflate, etc, and it would provide the only means to "simulate" that kind of behavior with an extension function.

Just as a side-note, here is one example where "this ref" might be useful, and IMHO still readable:

void SwapWith<T>(this ref T x, ref T y) {
   T tmp = x; x = y; y = tmp;
}
Amphisbaena answered 5/12, 2012 at 8:0 Comment(1)
Just as a side note: we can now do swap with quite easily: (x, y) => (y, x);.Woozy
I
3

I agree that it useful for struct

So I want to propose that for making extension method for struct. this keyword should always pass struct by reference

class always pass by reference anyway. And whenever we create extension method we want it to behave like a real method. So real method of class and struct can modified its value. Extension method should be able to too

Infield answered 12/5, 2015 at 7:42 Comment(0)
D
0

This would mean that calling myObject.ChangeWithExtentionMethod(otherObject) would actually have the potential to change myObject's value. IMO, that wouldn't make for very readable code when you can instead achieve the desired effect by using a regular non-extension method with a ref.

EDIT: My point is, the method call should require you to use the ref keyword any time that you're passing something by reference. Using ref with an extension method's 'this' parameter would violate that behavior.

Dimitris answered 11/4, 2010 at 20:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.