代码之家  ›  专栏  ›  技术社区  ›  Jakob Gade

扩展方法不允许通过引用传递参数吗?

  •  22
  • Jakob Gade  · 技术社区  · 15 年前

    是否真的不可能在C中创建一个扩展方法,在C中实例作为引用传递?

    这里是一个示例vb.net控制台应用程序:

    Imports System.Runtime.CompilerServices
    
    Module Module1
      Sub Main()
        Dim workDays As Weekdays
    
        workDays.Add(Weekdays.Monday)
        workDays.Add(Weekdays.Tuesday)
    
        Console.WriteLine("Tuesday is a workday: {0}", _ 
          CBool(workDays And Weekdays.Tuesday))
        Console.ReadKey()
      End Sub
    End Module
    
    <Flags()> _
    Public Enum Weekdays
      Monday = 1
      Tuesday = 2
      Wednesday = 4
      Thursday = 8
      Friday = 16
      Saturday = 32
      Sunday = 64
    End Enum
    
    Module Ext
      <Extension()> _
      Public Sub Add(ByRef Value As Weekdays, ByVal Arg1 As Weekdays) 
        Value = Value + Arg1
      End Sub
    End Module
    

    注意,value参数是通过byref传递的。

    和(几乎)在c中相同:

    using System;
    
    namespace CS.Temp
    {
      class Program
      {
        public static void Main()
        {
          Weekdays workDays = 0;
    
          workDays.Add(Weekdays.Monday); // This won't work
          workDays.Add(Weekdays.Tuesday);
    
          // You have to use this syntax instead...
          // workDays = workDays | Weekdays.Monday;
          // workDays = workDays | Weekdays.Tuesday;
    
          Console.WriteLine("Tuesday is a workday: {0}", _ 
            System.Convert.ToBoolean(workDays & Weekdays.Tuesday));
          Console.ReadKey();
        }
      }
    
      [Flags()]
      public enum Weekdays : int
      {
        Monday = 1,
        Tuesday = 2,
        Wednesday = 4,
        Thursday = 8,
        Friday = 16,
        Saturday = 32,
        Sunday = 64
      }
    
      public static class Ext
      {
        // Value cannot be passed by reference? 
        public static void Add(this Weekdays Value, Weekdays Arg1) 
        {
          Value = Value | Arg1;
        }
      }
    }
    

    这个 Add 扩展方法在C中不起作用,因为我不能使用 ref 关键字。有什么解决办法吗?

    3 回复  |  直到 15 年前
        1
  •  13
  •   Gishu    15 年前

    不,在C,你 不能 指定任何修饰符(如“out”或 ref 除外) this 对于扩展方法的第一个参数-您可以为其他参数。 不熟悉VB语法,但似乎使用声明性方法来标记扩展方法。

    当你叫它的时候,你 指定第一个 参数。因此,将参数标记为out或ref没有意义,因为在调用时不能像对普通方法那样指定修饰符。

    void MyInstanceMethod(ref SomeClass c, int data) { ... } // definition
    
    obj.MyInstanceMethod(ref someClassObj, 10);              // call
    
    void MyExtensionMethod(this SomeClass c, int data) {.... } // defn
    
    c.MyExtensionMethod(10);                                 // call
    

    我认为您在这里遇到的问题与不可变的值类型有关。如果工作日是一种引用类型,那么它就可以工作了。对于不可变类型(结构),defacto方法是返回具有所需值的新实例。例如,请参见结构datetime上的add方法,它返回一个新的datetime实例,其值=receiver datetime实例的值+param值。

    public DateTime Add( TimeSpan value )
    
        2
  •  11
  •   Sam Harwell    15 年前

    你在制造一个可变不变的结构。它打破了人们期望在C中看到的内容,但如果必须这样做,则可以始终直接调用该方法:

    Ext.Add(ref value, arg1);
    

    任何扩展方法都可以直接调用。

    此外,澄清:

    SomeReferenceType value = ...;
    SomeReferenceType copy = value;
    value.ExtensionMethodByRef(...);
    // this failing is semantically ridiculous for reference types, which
    // is why it makes no sense to pass a `this` parameter by ref.
    object.ReferenceEquals(value, copy);
    
        3
  •  4
  •   Thomas Levesque    15 年前

    奇怪的是,vb.net允许这样做,而c却没有……

    但是,尽管从技术的角度来看它可能是有意义的(因为扩展方法只是一个静态方法),但我认为它感觉不正确,因为扩展方法被用作实例方法,并且实例方法不能修改 this 参考文献。