无法在扩展方法中使用ref和out作为第一个("this")参数?

Hun*_*hpu 28 .net c# extension-methods

为什么禁止Extension Methodref修饰符调用?

这个是可能的:

public static void Change(ref TestClass testClass, TestClass testClass2)
{
    testClass = testClass2;
}
Run Code Online (Sandbox Code Playgroud)

这不是:

public static void ChangeWithExtensionMethod(this ref TestClass testClass, TestClass testClass2)
{
    testClass = testClass2;
}
Run Code Online (Sandbox Code Playgroud)

但为什么?

Jon*_*eet 21

你必须指定refout明确.你会怎么用扩展方法做到这一点?而且,你真的想要吗?

TestClass x = new TestClass();
(ref x).ChangeWithExtensionMethod(otherTestClass);
// And now x has changed?
Run Code Online (Sandbox Code Playgroud)

或者您是否希望不必指定ref部件,仅用于扩展方法中的第一个参数?

对我来说,这听起来很奇怪,说实话,以及不可读(或至少难以预测)代码的配方.

  • @JonSkeet:我不喜欢模式`foo = foo.WithSomeChange()`.它不能成为线程安全的,如果`foo`不是一个简单的变量,那就很尴尬(有时很危险).将`foo`作为`ref`参数传递给`MakeSomeChange`方法允许它是线程安全的,清楚地表明该方法将改变`foo`,并将导致编译器禁止危险的用途.我书中的所有重大胜利.如果能够使用突出显示所执行操作的语法而不是静态实用程序类的名称来保持这些获胜,那将是很好的. (11认同)
  • "你必须明确指定"是一个人为的编译器要求,可以在这里删除.这会让它变得更加模糊. (4认同)
  • @Jon Skeet-对于从VB进入C#的我们来说,这是一个非常有效的问题。仅仅因为您不同意而已,并不意味着就不值得讨论。拒绝这样的人的意见对我来说似乎有点精英。 (3认同)
  • @Jon Skeet:我指的是“我认为不值得进行任何此类讨论”。我确实值得。尽管我没有提到它,但我发现“您是否出于兴趣而浏览我古老的文章以寻求不同意见?”,不管怎么说,都是不必要的。我不认识您,也不知道您可能拥有超级猫的任何历史,但这并不能改变这个问题对于VB人士的真正价值。VB是一种非常实用的语言,看到C#纯粹主义者争辩那些只会使代码变得比实际更教条的概念可能会令人震惊。 (3认同)
  • 但是这个原因不再适用于C#4.事实上,在VB中你*可以*已经使用`ByRef`扩展方法,如果我没记错的话. (2认同)
  • @supercat-我也不喜欢foo = foo.WithSomeChanges模式。在我看来,使用ByRef扩展方法会产生更优美的代码,允许方法调用,而不仅仅是函数调用。我还使用它们来将Nothing(空)检查推送到扩展方法中,因此不必在调用代码中一遍又一遍地编写它。我很高兴您提出这个问题,因为我遇到了同样的问题。我正在尝试使用C#,但是类似的问题却令人沮丧。 (2认同)
  • @YannDuran:我真的希望有一种针对不同样式的成员调用的定义语法(类似于“。”和“->”运算符之间的C区别)。除其他事项外,如果`readonlystruct.methodOrProperty'从未合法,但编译器需要类似`(readonlystruct).methodOrProperty` [以明确表明已创建临时副本]之类的东西,这会扼杀有关“可变结构”行为]。对于foo = foo.WithX(5);我也很喜欢`foo。= WithX(5);`之类的东西。 (2认同)
  • 从 C# 7.2 开始,允许使用值类型的“this ref”扩展方法。 (2认同)

drw*_*ode 11

我同意Jon Skeet等人的答案.关于如何允许"ref this"扩展方法可以使代码更加模糊.但是,如果您查看.Net Framework中的某些命名空间,则在结构上调用的方法通常会对其进行更改.

以System.Drawing结构(Point,Rectangle等)为例.其中每一个都有改变结构本身的方法(例如Offset,Inflate等).我不是说这是一个好主意,事实上我个人觉得Offset,Inflate等变异结构本身而不是返回新结构非常烦人,我知道你们中的一些人反对改变结构的想法一般.

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

但是就结构而言,允许"this ref"不会显着降低代码可读性而不仅仅是Rectangle.Inflate等,它将提供用扩展函数"模拟"这种行为的唯一方法.

正如旁注,这里有一个例子,其中"这个参考" 可能是有用的,恕我直言仍然可读:

void SwapWith<T>(this ref T x, ref T y) {
   T tmp = x; x = y; y = tmp;
}
Run Code Online (Sandbox Code Playgroud)


小智 9

我知道这是一个老问题。但事情已经发生了变化。如果有人正在寻找这个。

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

这仅适用于值类型 ( struct),而不适用于引用类型 ( class, interface, record)。

来源:Microsoft Docs,“扩展方法(C# 编程指南)——扩展预定义类型”

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @bkqc 字符串不是“介于”结构和类之间,它只是一个不可变的类。使用 ref 是没有意义的,因为它已经通过引用传递了。 (2认同)