【问题标题】:Extension methods on a struct结构上的扩展方法
【发布时间】:2011-01-11 09:46:12
【问题描述】:

你可以为结构添加扩展方法吗?

【问题讨论】:

  • 您是否尝试过在结构上添加扩展方法?请注意,当您找到问题的答案时,您不应该删除问题,其他人可能会想知道同样的事情。是的,你可以声明它们,但是改变结构中的东西不会像你期望的那样起作用,除非你期望它不起作用。
  • 是的,我试过了,但没用。
  • 谢谢你成功了,我的错
  • 我认为这个问题可能不够精确,无法捕捉到答案的微妙之处,因为结构扩展方法确实不像对象扩展方法,因为它们接收结构的副本而不是对结构的引用。更好的表达方式可能是“在结构上定义扩展方法意味着什么?”

标签: c# .net struct extension-methods


【解决方案1】:

是的,您可以在结构上添加扩展方法。根据扩展方法的定义,您可以轻松实现它。下面是 int 上的扩展方法示例

namespace ExtensionMethods
{
    public static class IntExtensions
     {
        public static bool IsGreaterEqualThan(this int i, int value)
        {
            return i >= value;
        }
    }
}

【讨论】:

  • 请注意这一点,因为结构是按值传递给扩展方法的(就像结构想要做的那样)。因此,您在扩展方法中对结构所做的任何修改都将丢失——除非您当然返回该结构(然后对传回的结构做一些事情,例如将其重新分配给自身)。没有办法对结构执行扩展方法并通过引用传递(在 C# 中)。
  • 看来我不能扩展 System.Drawing.Rectangle 这也是一个结构,并用 Serializable 和 ComVisible 和 TypeConverter 装饰。
  • BrainSlug83 C# 7.2+ 现在支持结构扩展方法中的引用。
  • 这非常有用,和 ref 一样,我们可以细分一个 Rect - 修改传入的 Rect 并返回新的 Rect - 在单个扩展方法中。
【解决方案2】:

可以为结构添加扩展方法,但有一个重要的警告。普通结构方法方法接受 this 作为 ref 参数,但 C# 不允许定义这样做的扩展方法。虽然改变 this 的 struct 方法可能有些危险(因为编译器将允许在只读结构上调用 struct 方法,但通过值传递 this),如果小心谨慎,它们有时也很有用确保它们仅在适当的上下文中使用。

顺便说一句,vb.net 确实允许扩展方法接受 this 作为 ByRef 参数,无论它是类、结构还是未知类别的泛型。这在接口可能由结构实现的某些情况下会很有帮助。例如,如果尝试在List<string>.Enumerator 类型的变量上调用一个扩展方法,该方法采用IEnumerator<string> 类型的this 参数,或按值采用受IEnumerator<string> 约束的泛型的this 参数,如果该方法尝试推进枚举数,则任何推进都将在该方法返回时撤消。但是,通过引用采用受约束的泛型的扩展方法(可能在 vb.net 中)将正常运行。

【讨论】:

  • 那么在 C# 中使用一个的例子是什么? public static Rect CreateRectFromPercents(this Rect rect) 例如不起作用。
  • @MichaelTaylor3D:Rect 是什么?你的意思是System.Drawing.Rectangle 还是一些自定义类型?
  • 它是我使用的 api 的一部分的结构。我只是用它作为例子。 api阻止我直接修改它,所以我希望使用扩展方法。我读过它可能用于结构,但我还没有看到一个例子。你的解释是我能找到的最接近的。
  • 可以编写一个扩展方法WithHeight,它接受Rect 并返回一个除了Height 参数外相同的新方法。出于好奇,API 是否通过阻止对结构字段的方便访问来获得任何好处?我发现人们应该将结构视为“假装类”而不是认识到它们是应该以不同方式处理的不同类型的实体的想法令人讨厌。虽然很少有类应该公开任何字段,但我建议绝大多数结构类型应该公开所有字段。
  • @MichaelTaylor3D:可以肯定的是,公开公共字段会使结构不像类对象那样表现得更加明显,但应该更明显地表明它们 不是 i> 类对象,不应该期望表现得像它们。在大多数情况下,结构是合适的,需要的是一个包含语义相关但独立值的少数变量的集合。开场结构非常适合,因为这正是开场结构的。试图假装结构是别的东西,并因此绕过限制,...
【解决方案3】:

对于未来的 Google 员工(和 Bingers),这里有一些扩展结构的代码。此示例将值转换为 double 类型。

public static class ExtensionMethods {

   public static double ToDouble<T>(this T value) where T : struct {
      return Convert.ToDouble(value);
   }
}

在此之后,您可以像使用 ToString() 一样使用 ToDouble()。小心溢出等转换项。

【讨论】:

    【解决方案4】:

    是的,您可以在结构/值类型上定义扩展方法。但是,它们的行为与引用类型上的扩展方法不同。

    例如,以下 C# 代码中的 GetA() 扩展方法接收结构的副本,而不是对结构的引用。这意味着结构上的 C# 扩展方法不能修改原始结构内容。

    public static class TestStructExtensionMethods {
        public struct FooStruct {
            public int a;
        }
        public static int GetA(this FooStruct st) {
            return st.a;
        }
    }
    

    为了修改结构内容,结构参数需要声明为“ref”。但是,C# 中不允许使用“this ref”。我们能做的最好的就是静态非扩展方法,例如:

    // this works, but is inefficient, because it copies the whole FooStruct
    // just to return a
    public static int GetA(ref FooStruct st) {
        return st.a;
    }
    

    在 VB.NET 中,您可以将其创建为 ByRef 结构扩展方法,因此它可以修改原始结构:

    ' This is efficient, because it is handed a reference to the struct
    <Extension()> _ 
    Public Sub GetA(ByRef [me] As FooStruct) As Integer
        Return [me].a
    End Sub
    
    ' It is possible to change the struct fields, because we have a ref
    <Extension()> _ 
    Public Sub SetA(ByRef [me] As FooStruct, newval As Integer) 
        [me].a = newval
    End Sub
    

    【讨论】:

    • VB.NET 允许扩展方法接受 ref 的结构是一件好事,但事实上 VB 会默默地复制 ref 无法传递的东西并将传递复印件而不是原件。如果有办法告诉 VB.NET 不要这样做(代码应该拒绝编译),那么这种扩展方法在语义上将优于修改 this 的结构实例方法。我不知道为什么 MS 没有提供任何方法可以指示它们是否应该可以在编译器生成的临时结构上调用。
    • "... 但事实上 VB 会默默地复制无法通过 ref 传递的东西,并且会传递副本而不是原件”——VB 什么时候这样做? -- "byref" 被编译成与 C# 中的 "ref" 相同的 IL 代码 -- 我的理解是任何东西都可以通过 .NET 中的 ref 传递 -- 唯一被复制的东西是值类型(并且只有当它们不是由 ref 传递的)。
    • @BrainSlugs83:尝试在 VB.net 中通过引用传递读/写属性。该属性只是 getter 函数的返回值,而不是变量......所以通过引用传递不起作用。 C# 不允许。 VB 将通过复制值来伪造传递引用,并通过引用传递 copy ......然后当函数返回时,将属性设置为新值。如果您遵循 LoD 并保持结构不可变,则 大部分 可以工作,并保持属性半透明......但是当涉及到子对象和可变值类型时,可能会以令人惊讶的方式中断。
    • 我不知道 VB 可以做到这一点。听起来这不是一个真正的“byref”调用,但见鬼 - 对我来说,这实际上听起来就像通过引用传递属性 应该 的工作原理.. - 显然,如果你用它做愚蠢的废话,你的代码会被破坏——但老实说,我真的希望 C# 有能力做到这一点。相反,每次我想通过引用传递属性时,我都必须编写 4 行代码——但是——正如我之前所怀疑的那样,这完全是题外话。
    猜你喜欢
    • 2017-09-01
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-22
    相关资源
    最近更新 更多