C# 无法对扩展方法中的第一个(“this”)参数使用ref和out?

C# 无法对扩展方法中的第一个(“this”)参数使用ref和out?,c#,.net,extension-methods,C#,.net,Extension Methods,为什么禁止使用ref修饰符调用扩展方法 这是可能的: public static void Change(ref TestClass testClass, TestClass testClass2) { testClass = testClass2; } 而这个不是: public static void ChangeWithExtensionMethod(this ref TestClass testClass, TestClass testClass2) { testCla

为什么禁止使用
ref
修饰符调用
扩展方法

这是可能的:

public static void Change(ref TestClass testClass, TestClass testClass2)
{
    testClass = testClass2;
}
而这个不是:

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

但是为什么呢?

这意味着调用
myObject.changewithextensionmethod(otherObject)
实际上可能会更改
myObject
的值。在我看来,如果您可以通过使用带有ref的常规非扩展方法来实现所需的效果,那么这将不会产生非常可读的代码


编辑:我的观点是,方法调用应该要求您在任何时候通过引用传递某些内容时使用ref关键字。将ref与扩展方法的“this”参数一起使用将违反该行为。

必须明确指定
ref
out
。您将如何使用扩展方法来实现这一点?此外,你真的想这样做吗

或者您是否希望不必仅为扩展方法中的第一个参数指定
ref
部分


老实说,对我来说,这听起来很奇怪,而且是一个不可读(或至少很难预测)代码的配方。

我同意Jon Skeet等人关于允许“ref this”扩展方法如何使代码更加模糊的回答。但是,如果您查看.Net Framework中的某些名称空间,在结构上调用的方法修改它是很常见的

以System.Drawing结构(点、矩形等)为例。每一种方法(如偏移、膨胀等)都会改变结构本身。我并不是说这是个好主意,事实上,我个人觉得偏移、膨胀等改变结构本身而不是返回新的结构非常烦人,我知道你们中的一些人通常反对可变结构的想法

我怀疑在任何情况下调用引用类型的方法都会改变引用(除非是使用
String
类,我可以想象在这种情况下可能会有一些编译器魔法来切换引用以执行内部调用等)。因此,防止“this ref”与引用类型一起使用是有意义的,因为更改引用将是调用方法的完全非标准副作用

但是对于结构来说,允许“this ref”不会像矩形、膨胀等那样显著降低代码的可读性,它将提供使用扩展函数“模拟”这种行为的唯一方法

作为旁注,这里有一个例子,其中“this ref”可能很有用,IMHO仍然可读:

void SwapWith<T>(this ref T x, ref T y) {
   T tmp = x; x = y; y = tmp;
}
void SwapWith(此参考x,参考y){
T tmp=x;x=y;y=tmp;
}

我同意它对struct有用

所以我想提出,为struct制作扩展方法。此关键字应始终通过引用传递struct

类始终通过引用传递。无论何时创建扩展方法,我们都希望它的行为像一个真实的方法。所以类和结构的实际方法可以修改其值。扩展方法也应该能够在C#7.2中使用

,您可以对结构使用ref扩展方法


我知道这是一个老问题。但情况发生了变化。万一有人在找这个

从C#7.2开始,可以将ref修饰符添加到第一个 扩展方法的参数。添加ref修饰符意味着 第一个参数通过引用传递。这使您能够编写 更改正在扩展的结构状态的扩展方法

来源:


您确定需要显式的
ref
?我希望它是由
这个
自动生成的-非引用扩展方法没有任何意义。但是如果我没有弄错的话,它们是非引用的。@MarcelJackwerth ref参数与引用类型参数不同。ref参数传递调用方的引用(或指针)本身。使用ref可以更新引用以指向其他对象。如果没有它(对于引用类型),您可以更新对象本身,但不能更新对它的引用。“您必须明确指定”是一个人工编译器要求,可以在此处删除。这会使它更加模糊。@JonSkeet:我不喜欢模式
foo=foo.WithSomeChange()
。它不能保证线程安全,如果
foo
不是一个简单的变量,那么它就很麻烦(有时很危险)。将
foo
作为
ref
参数传递给
MakeSomeChange
方法可以使其成为线程安全的,这表明该方法将要更改
foo
,并将导致编译器禁止危险的使用。我书中的所有重大胜利。如果能够用一种突出显示所操作对象的语法而不是静态实用程序类的名称来保持这些胜利,那就太好了。@Jon Skeet-对于我们这些从VB来到C#的人来说,这是一个非常有效的问题。仅仅因为你不同意并不意味着它不值得讨论。像那样无视别人的意见对我来说似乎有点精英主义。@supercat-我也不喜欢foo=foo.WithSomeChanges模式。在我看来,使用ByRef扩展方法产生的代码要优雅得多,允许方法调用&而不仅仅是函数调用。我还使用它们来将无需检查(null)推送到扩展方法中,因此不必在调用代码中反复写入。我个人很高兴你问了这个问题,因为我刚刚遇到了同样的问题。我正试图转移到C#,但像这样的问题非常令人沮丧。@Jon Skeet:我指的是“我认为这些都不值得讨论”。我觉得这是值得的。虽然我没有提到这一点,但我发现“出于兴趣,你会浏览我的古代帖子,寻找不同意的东西吗?”,无论措辞多么礼貌,都是没有必要的。我不认识你,我不知道你和supercat之间有什么历史,但是
void SwapWith<T>(this ref T x, ref T y) {
   T tmp = x; x = y; y = tmp;
}
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();
    }
}