C#扩展方法是否允许通过引用传递参数?

时间:2009-08-11 08:43:43

标签: c# vb.net extension-methods byref

是否真的无法在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 个答案:

答案 0 :(得分:13)

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

当您致电时,指定第一个this参数。因此将参数标记为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

我认为你在这里遇到的麻烦与价值类型是不可变的有关。如果工作日是一个参考类型,它会好起来的。对于不可变类型(结构),事实上的方法是返回具有所需值的新实例。例如。请参阅结构DateTime上的Add方法,它返回一个新的DateTime实例,其值= receiver DateTime实例的值+ param值。

public DateTime Add( TimeSpan value )

答案 1 :(得分:11)

Yikes - 你正在制作一个可变的不可变结构。它打破了人们期望在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);

答案 2 :(得分:4)

奇怪的是,VB.NET允许这样做而C#没有......

然而,尽管从技术角度来看它可能是有意义的(因为扩展方法只是一种静态方法),我认为它感觉不对,因为扩展方法被用作实例方法,并且实例方法无法修改this引用。