22

インスタンスが参照として渡される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これに対する回避策はありますか?

4

3 に答える 3

13

いいえ。C# では、拡張メソッドの最初のパラメーター以外の修飾子 (「out」や など) を指定することはできません。他のパラメーターには指定できます。VB 構文に精通していませんが、拡張メソッドをマークするために宣言的アプローチを使用しているようです。refthis

呼び出すときは、最初のパラメーターを指定しません。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

ここで発生している問題は、値の型が不変であることに関連していると思います。Weekdays が参照型の場合は問題ありません。不変型 (構造体) の場合、デファクトの方法は、必要な値を持つ新しいインスタンスを返すことです。たとえば、DateTime 構造体の Add メソッドを参照してください。値 = 受信側の DateTime インスタンスの値 + パラメータ値の新しい DateTime インスタンスを返します。

public DateTime Add( TimeSpan value )
于 2009-08-11T09:29:20.607 に答える
11

うーん-可変で不変の構造体を作成しています。これは、人々が 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);
于 2009-08-11T08:46:47.157 に答える
5

奇妙なことに、VB.NET ではこれが許可され、C# では許可されません...

ただし、技術的な観点からは理にかなっているかもしれませんが (拡張メソッドは単なる静的メソッドであるため)、拡張メソッドはインスタンス メソッドであるかのように使用され、インスタンス メソッドは参照を変更しないでthisください。

于 2009-08-11T08:48:33.923 に答える