Разве методы расширения С# не позволяют передавать параметры по ссылке?
Неужели невозможно создать метод расширения в С#, где экземпляр передан как ссылка?
Вот пример консольного приложения 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
Обратите внимание, что параметр значения передан ByRef.
И (почти) то же самое в С#:
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
не работает в С#, потому что я не могу использовать ключевое слово ref
. Есть ли обходной путь для этого?
Ответы
Ответ 1
Нет. В С# вы не можете указывать любые модификаторы (например, '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
Я думаю, что проблема, с которой вы здесь сталкиваетесь, связана с неизменяемыми типами значений. Если в будние дни был ссылочный тип, все будет хорошо. Для неизменяемых типов (structs) метод defacto должен возвращать новый экземпляр с требуемым значением. Например. См. Метод Add в структуре DateTime, он возвращает новый экземпляр DateTime, значение value = получателя DateTime значение + значение параметра.
public DateTime Add( TimeSpan value )
Ответ 2
Yikes - вы создаете изменяемую неизменяемую структуру. Это ломает то, что люди ожидают увидеть на С#, но если вам нужно, вы всегда можете напрямую вызвать метод:
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
Странно, что VB.NET позволяет это, а С# не...
Однако, хотя это может иметь смысл с технической точки зрения (поскольку метод расширения является только статическим методом), я думаю, что это не так, потому что методы расширения используются так, как если бы они были методами экземпляра, и методы экземпляра не могут изменять ссылку this
.