【问题标题】:Impossible to use ref and out for first ("this") parameter in Extension methods?扩展方法中的第一个(“this”)参数无法使用 ref 和 out?
【发布时间】:2011-02-06 19:07:33
【问题描述】:

为什么禁止使用ref修饰符调用Extension Method

这个是可能的:

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

而这个不是:

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

但是为什么呢?

【问题讨论】:

  • 您确定需要明确的ref 吗?我希望它是由this“自动生成”的——非引用扩展方法没有任何意义。
  • 但如果我没记错的话,它们是非参考的。
  • @MarcelJackwerth 引用参数与引用类型参数不同。 ref 参数本身传递调用者的引用(或指针)。使用 ref 您可以更新引用以指向其他对象。没有它(对于引用类型),您可以更新对象本身,但不能更新对它的引用。

标签: c# .net extension-methods


【解决方案1】:

您必须明确指定refout。您将如何使用扩展方法来做到这一点?此外,你真的想要吗?

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

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

说实话,这对我来说听起来很奇怪,并且是不可读(或至少难以预测)代码的秘诀。

【讨论】:

  • “您必须明确指定...”是一个人工编译器要求,可以在此处删除。但这会让它更加模糊。
  • @JonSkeet:我不喜欢foo = foo.WithSomeChange() 的模式。它不能成为线程安全的,如果foo 不是一个简单的变量,它会很尴尬(有时很危险)。将foo 作为ref 参数传递给MakeSomeChange 方法允许它是线程安全的,清楚地表明该方法将更改foo,并且将导致编译器将禁止危险使用。我书中的所有重大胜利。如果能够通过突出显示正在执行的操作的语法而不是静态实用程序类的名称来保持这些胜利,那就太好了。
  • @Jon Skeet - 对于我们这些从 VB 来到 C# 的人来说,这是一个非常有效的问题。仅仅因为你不同意并不意味着它不值得讨论。像这样否定别人的意见对我来说似乎有点精英化。
  • @supercat - 我也不喜欢 foo = foo.WithSomeChanges 模式。在我看来,使用 ByRef 扩展方法会产生更优雅的代码,允许方法调用而不仅仅是函数调用。我还使用它们能够将 Nothing (null) 的检查推送到扩展方法中,因此不必在调用代码中重写它。我很高兴你问了这个问题,因为我刚刚遇到了同样的问题。我正在尝试迁移到 C#,但是这样的问题非常令人沮丧。
  • @Jon Skeet:我指的是“我认为这不值得深入研究”。我确实觉得值得。虽然我没有提到它,但我发现“你是否出于兴趣浏览我的古老帖子寻找不同意的东西?”,无论措辞多么礼貌,都是不必要的。我不认识你,也不知道你可能有任何关于 supercat 的历史,但这并不能改变这个问题对于来自 VB 的人来说具有真正价值的事实。 VB 是一门非常实用的语言,看到 C# 纯粹主义者争论的概念只会让代码更加教条而不是实用,这可能会令人震惊。
【解决方案2】:
【解决方案3】:

我同意 Jon Skeet 等人的回答。关于允许“ref this”扩展方法如何使代码更加晦涩。但是,如果您查看 .Net 框架中的某些命名空间,通常会在结构上调用方法来更改它。

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

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

但是对于结构体,允许“this ref”不会比 Rectangle.Inflate 等显着降低代码的可读性,并且它将提供使用扩展函数“模拟”这种行为的唯一方法。

作为旁注,这里有一个示例,其中“this ref”可能很有用,而且恕我直言仍然可读:

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

【讨论】:

  • 顺便说一句:我们现在可以很容易地进行交换:(x, y) =&gt; (y, x);
【解决方案4】:

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

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

这仅适用于值类型 (struct),而不是引用类型 (classinterfacerecord)。

来源: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();
    }
}

【讨论】:

    【解决方案5】:

    我同意它对结构有用

    所以我想建议为 struct 制作扩展方法。这个关键字应该总是通过引用传递结构

    类总是通过引用传递。每当我们创建扩展方法时,我们都希望它表现得像一个真实的方法。所以类和结构的真实方法可以修改它的值。扩展方法应该也可以

    【讨论】:

      【解决方案6】:

      这意味着调用myObject.ChangeWithExtentionMethod(otherObject) 实际上有可能改变myObject 的值。 IMO,当您可以通过使用带有 ref 的常规非扩展方法来实现所需的效果时,这不会产生非常可读的代码。

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

      【讨论】:

        猜你喜欢
        • 2023-01-11
        • 1970-01-01
        • 2015-11-16
        • 2021-07-03
        • 1970-01-01
        • 2019-12-23
        • 1970-01-01
        相关资源
        最近更新 更多