Разве методы расширения C # не позволяют передавать параметры по ссылке? - PullRequest
22 голосов
/ 11 августа 2009

Действительно ли невозможно создать метод расширения в 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 ]

13 голосов
/ 11 августа 2009

Нет. В C # вы не можете указать какие-либо модификаторы (например, 'out' или ref), кроме 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

Я думаю, что проблема, с которой вы здесь сталкиваетесь, связана с неизменностью типов значений. Если бы Будни были ссылочным типом, все бы хорошо. Для неизменяемых типов (структур) способ де-факто - возвращать новый экземпляр с требуемым значением. Например. См. Метод Add в структуре DateTime, он возвращает новый экземпляр DateTime, значение которого = значение экземпляра DateTime получателя + значение параметра.

public DateTime Add( TimeSpan value )
11 голосов
/ 11 августа 2009

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);
4 голосов
/ 11 августа 2009

Странно, что VB.NET позволяет это, а C # - нет ...

Однако, хотя это может иметь смысл с технической точки зрения (поскольку метод расширения является просто статическим методом), я думаю, что это не правильно, поскольку методы расширения используются так, как если бы они были методами экземпляра, и Методы экземпляра не могут изменить ссылку this.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...