web-dev-qa-db-fra.com

Surcharge d'opérateur avec les méthodes d'extension C #

J'essaie d'utiliser des méthodes d'extension pour ajouter une surcharge d'opérateur à la classe C # StringBuilder. Plus précisément, étant donné StringBuildersb, j'aimerais que sb += "text" Devienne équivalent à sb.Append("text").

Voici la syntaxe pour créer une méthode d'extension pour StringBuilder:

public static class sbExtensions
{
    public static StringBuilder blah(this StringBuilder sb)
    {
        return sb;
    }
} 

Il ajoute avec succès la méthode d'extension blah à StringBuilder.

Malheureusement, la surcharge de l'opérateur ne semble pas fonctionner:

public static class sbExtensions
{
    public static StringBuilder operator +(this StringBuilder sb, string s)
    {
        return sb.Append(s);
    }
} 

Entre autres problèmes, le mot clé this n'est pas autorisé dans ce contexte.

Est-il possible d'ajouter des surcharges d'opérateur via des méthodes d'extension? Si oui, quelle est la bonne façon de procéder?

165
Jude Allred

Ce n'est actuellement pas possible, car les méthodes d'extension doivent être dans des classes statiques et les classes statiques ne peuvent pas avoir de surcharge d'opérateur.

Mads Torgersen, langage C # PM dit:

... pour la version Orcas, nous avons décidé d'adopter une approche prudente et d'ajouter uniquement des méthodes d'extension régulières, par opposition aux propriétés d'extention, aux événements, aux opérateurs, aux méthodes statiques, etc. une conception syntaxiquement minimale qui ne pouvait pas être facilement imitée pour certains des autres types de membres.

Nous sommes de plus en plus conscients que d'autres types de membres d'extension pourraient être utiles, et nous reviendrons donc sur ce problème après Orcas. Aucune garantie cependant!

Modifier:

Je viens de remarquer, Mads a écrit plus dans le même article :

Je suis désolé de signaler que nous ne le ferons pas dans la prochaine version. Nous avons pris les membres de l'extension très au sérieux dans nos plans et avons déployé beaucoup d'efforts pour essayer de les faire bien, mais à la fin nous n'avons pas pu le faire assez facilement et avons décidé de céder la place à d'autres fonctionnalités intéressantes.

C'est toujours sur notre radar pour les versions futures. Ce qui sera utile, c'est si nous obtenons une bonne quantité de scénarios convaincants qui peuvent aider à conduire la bonne conception.


Cette fonctionnalité est actuellement sur la table (potentiellement) pour C # 8.0. Mads parle un peu plus de l'implémenter ici .

147
Jacob Krall

Si vous contrôlez les endroits où vous souhaitez utiliser cet "opérateur d'extension" (ce que vous faites normalement avec les méthodes d'extension de toute façon), vous pouvez faire quelque chose comme ceci:

class Program {

  static void Main(string[] args) {
    StringBuilder sb = new StringBuilder();
    ReceiveImportantMessage(sb);
    Console.WriteLine(sb.ToString());
  }

  // the important thing is to use StringBuilderWrapper!
  private static void ReceiveImportantMessage(StringBuilderWrapper sb) {
    sb += "Hello World!";
  }

}

public class StringBuilderWrapper {

  public StringBuilderWrapper(StringBuilder sb) { StringBuilder = sb; }
  public StringBuilder StringBuilder { get; private set; }

  public static implicit operator StringBuilderWrapper(StringBuilder sb) {
    return new StringBuilderWrapper(sb);
  }

  public static StringBuilderWrapper operator +(StringBuilderWrapper sbw, string s) { 
      sbw.StringBuilder.Append(s);
      return sbw;
  }

} 

La classe StringBuilderWrapper déclare un opérateur de conversion implicite à partir d'un StringBuilder et déclare la valeur souhaitée + opérateur. De cette façon, un StringBuilder peut être passé à ReceiveImportantMessage, qui sera converti silencieusement en StringBuilderWrapper, où le + L'opérateur peut être utilisé.

Pour rendre ce fait plus transparent pour les appelants, vous pouvez déclarer ReceiveImportantMessage comme prenant un StringBuilder et utiliser simplement du code comme ceci:

  private static void ReceiveImportantMessage(StringBuilder sb) {
    StringBuilderWrapper sbw = sb;
    sbw += "Hello World!";
  }

Ou, pour l'utiliser en ligne où vous utilisez déjà un StringBuilder, vous pouvez simplement faire ceci:

 StringBuilder sb = new StringBuilder();
 StringBuilderWrapper sbw = sb;
 sbw += "Hello World!";
 Console.WriteLine(sb.ToString());

J'ai créé n article sur l'utilisation d'une approche similaire pour rendre IComparable plus compréhensible.

55
Jordão

Il semble que ce ne soit pas possible actuellement - un problème de rétroaction ouvert demande cette fonctionnalité sur Microsoft Connect:

http://connect.Microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=168224

suggérant qu'il pourrait apparaître dans une future version mais n'est pas implémenté pour la version actuelle.

8
Dylan Beattie

Hah! Je cherchais "surcharge d'opérateur d'extension" avec exactement le même désir, pour sb + = (chose).

Après avoir lu les réponses ici (et vu que la réponse est "non"), pour mes besoins particuliers, je suis allé avec une méthode d'extension qui combine sb.AppendLine et sb.AppendFormat, et semble plus ordonnée que non plus.

public static class SomeExtensions
{
    public static void Line(this StringBuilder sb, string format, params object[] args)
    {
        string s = String.Format(format + "\n", args);
        sb.Append(s);
    }

}

Et donc,

sb.Line("the first thing is {0}",first);
sb.Line("the second thing is {0}", second);

Pas une réponse générale, mais peut intéresser les futurs chercheurs qui envisagent ce genre de chose.

1
david van brink

Bien qu'il ne soit pas possible de faire les opérateurs, vous pouvez toujours simplement créer des méthodes Add (ou Concat), Subtract et Compare ....

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;    

namespace Whatever.Test
{
    public static class Extensions
    {
        public static int Compare(this MyObject t1, MyObject t2)
        {
            if(t1.SomeValueField < t2.SomeValueField )
                return -1;
            else if (t1.SomeValueField > t2.SomeValueField )
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }

        public static MyObject Add(this MyObject t1, MyObject t2)
        {
            var newObject = new MyObject();
            //do something  
            return newObject;

        }

        public static MyObject Subtract(this MyObject t1, MyObject t2)
        {
            var newObject= new MyObject();
            //do something
            return newObject;    
        }
    }


}
1
Chuck Rostance

Il est possible de le monter avec un wrapper et des extensions mais impossible de le faire correctement. Vous vous retrouvez avec des ordures qui vont totalement à l'encontre du but. J'ai un article quelque part ici qui le fait, mais ça ne vaut rien.

Btw Toutes les conversions numériques créent des ordures dans le générateur de chaînes qui doivent être corrigées. J'ai dû écrire un wrapper pour ce qui fonctionne et je l'utilise. Cela vaut la peine d'être lu.

0
will motil