2009-08-11 4 views
20

Est-il vraiment impossible de créer une méthode d'extension en C# où l'instance est passée en référence?Les méthodes d'extension C# ne permettent-elles pas de passer des paramètres par référence?

Voici un exemple de l'application de la console 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 

Notez le paramètre de la valeur est passée ByRef.

Et (presque) même en 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; 
    } 
    } 
} 

La méthode d'extension Add ne fonctionne pas en C# parce que je ne peux pas utiliser le mot-clé ref. Y at-il une solution de contournement pour cela?

+0

Juste pour l'amour de la fin: la bonne façon « Ajouter » une valeur d'un drapeau ENUM est '= valeur ou Arg1' sauf si vous voulez que l'ajout' Monday' deux fois se comporte comme l'ajout 'Tuesday'. La façon correcte de supprimer un indicateur est 'Value = (Value Or Arg1) Xor Arg1'. – LWChris

Répondre

12

Non, en C#, vous ne peut pas spécifier les modificateurs (comme « out » ou ref) autre que this pour le premier paramètre d'une méthode d'extension - vous pouvez pour les autres. Pas familier avec la syntaxe VB, mais il semble utiliser une approche déclarative pour marquer une méthode d'extension.

Lorsque vous appelez, vous ne le font pas spécifiez le premier paramètre this. Par conséquent le paramètre de marquage comme sur ou ne marche pas de sens que ref Vous ne pouvez pas spécifier le modificateur lorsque vous l'appelez comme vous le feriez pour les méthodes normales

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 

Je pense que le problème que vous rencontrez ici est lié à les types de valeur étant immuables. Si Weekdays était un type de référence, tout irait bien. Pour les types immuables (structs), la méthode defacto consiste à renvoyer une nouvelle instance avec la valeur requise. Par exemple. Voir la méthode Add sur la structure DateTime, elle renvoie une nouvelle instance DateTime dont la valeur = la valeur de l'instance DateTime du destinataire + la valeur param.

public DateTime Add(TimeSpan value) 
+0

En vb, on peut spécifier qu'une méthode d'extension prend le paramètre implicite 'this' (' Me') comme paramètre 'ByRef'; malheureusement, le compilateur continue à autoriser les structures en lecture seule à lui passer. L'une des principales plaintes concernant les types de valeurs mutables est que, puisqu'il n'y a rien pour indiquer quelles méthodes font muter 'this', elles permettent à ces méthodes d'être appelées inutilement sur des instances en lecture seule. Je trouve étrange que les implémenteurs de vb.net décident d'autoriser une méthode d'extension qui prend un paramètre 'ByRef' - en criant pratiquement" JE METTREAI L'ARGUMENT! "- dans un contexte en lecture seule. – supercat

11

Oui, vous faites une structure mutable immuable. Il brise ce que les gens attendent de voir en C#, mais si vous devez, vous pouvez toujours appeler directement la méthode:

Ext.Add(ref value, arg1); 

Toute méthode d'extension est directement appelable.

En outre, une clarification:

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

mutable immutable struct? –

+1

oui, Eric Lippert aimerait que;) –

+2

Les énumérations sont des valeurs immuables, mais en passant une par référence en tant que ce paramètre à une méthode d'extension le ferait se comporter comme une valeur mutable. –

4

étrange que VB.NET permet cela et C# ne pas ...

Cependant, bien qu'il puisse donner un sens d'un point de vue technique (depuis une méthode d'extension est juste une méthode statique), je pense qu'elle ne se sent pas bien, parce que les méthodes d'extension sont utilisées comme si elles étaient des méthodes d'instance, et les méthodes d'instance ne peuvent pas modifier la référence this.

+8

En fait, les méthodes d'instance * peuvent * modifier "ceci" dans les types de valeur. Bizarre mais vrai. –

+1

wow ... Je n'ai jamais réalisé ça! –

+3

Il est inutile de dire que cela SUCE un grand temps pour C#. –

Questions connexes