12 Stimmen

Wann werden Erweiterungsmethoden unterbrochen?

Wir diskutieren gerade darüber, ob Erweiterungsmethoden in .NET schlecht sind oder nicht. Oder unter welchen Umständen Extension-Methoden schwer zu findende Bugs einführen oder sich auf andere Weise unerwartet verhalten können.

Wir haben uns das ausgedacht:

  • Das Schreiben einer Erweiterungsmethode für Typen, die nicht unter Ihrer Kontrolle stehen (z.B. die Erweiterung von DirectoryInfo mit GetTotalSize(), etc...) ist schlecht, weil der Besitzer der API eine Methode einführen könnte, die unsere Erweiterung versteckt - und möglicherweise andere Randfälle hat. Zum Beispiel wird das Testen auf null in einer Erweiterungsmethode automatisch in eine NullReferenceException übersetzen, wenn die Erweiterungsmethode aufgrund des Ausblendens nicht mehr verwendet wird.

Frage:

  • Gibt es noch andere gefährliche Situationen als "Verstecken", an die wir nicht denken?

Bearbeiten:

Eine weitere sehr gefährliche Situation. Angenommen, Sie haben eine Erweiterungsmethode:

namespace Example.ExtensionMethods
{
    public static class Extension
    {
        public static int Conflict(this TestMe obj)
        {
            return -1;
        }
    }
}

Und benutzen Sie es:

namespace Example.ExtensionMethods.Conflict.Test
{
    [TestFixture]
    public class ConflictExtensionTest
    {
        [Test]
        public void ConflictTest()
        {
            TestMe me = new TestMe();
            int result = me.Conflict();
            Assert.That(result, Is.EqualTo(-1));
        }
    }
}

Beachten Sie, dass der Namespace, in dem Sie ihn verwenden, länger ist.

Jetzt referenzieren Sie damit eine dll:

namespace Example.ExtensionMethods.Conflict
{
    public static class ConflictExtension
    {
        public static int Conflict(this TestMe obj)
        {
            return 1;
        }
    }
}

Und Ihr Test wird scheitern! Er wird kompiliert, ohne dass ein Compilerfehler auftritt. Er wird einfach scheitern . Ohne dass Sie überhaupt "using Example.ExtensionMethods.Conflict" angeben müssen. Der Compiler wird den Namespace-Namen durchsuchen und Example.ExtensionMethods.Conflict.ConflictExtension vor Example.ExtensionMethods.Extension finden und diese verwenden ohne sich jemals über zweideutige Erweiterungsmethoden zu beschweren . Oh, das Grauen!

0 Stimmen

Ich musste ein Beispiel für ein neues Mitglied ausprobieren, das eine Erweiterungsmethode versteckt, ohne dass der Compiler einen Pieps von sich gibt, bevor ich es glauben konnte! Ich denke, D hat das mit seinem Konzept der "Überladungssätze" richtig gemacht.

9voto

Marc Gravell Punkte 970173

Einige Kuriositäten:

  • Erweiterungsmethoden können aufgerufen werden bei null Instanzen; dies kann verwirrend (aber manchmal nützlich) sein
  • das Problem des "Versteckens" ist ein großes Problem, wenn sie unterschiedliche Absichten haben
  • können Sie auch eine andere Erweiterungsmethode mit demselben Namen aus 2 verschiedenen Namensräumen erhalten; wenn Sie nur un der beiden Namensräume, könnte dies zu inkonsistentem Verhalten führen (je nachdem, welcher)...
  • ...aber wenn jemand eine ähnlich (gleiche Signatur) Erweiterungsmethode in einem zweiten Namespace, den Ihr Code verwendet, wird er zur Kompilierzeit abbrechen (mehrdeutig)

( bearbeiten ) Und natürlich gibt es die " Nullable<T> / new() Bombe" ( siehe hier )...

5voto

Chuck Conway Punkte 16031

Ich stimme nicht zu, der ganze Sinn von Erweiterungsmethoden ist es, Ihre Mitglieder zu Blackbox-Klassen hinzuzufügen. Wie bei allem anderen gibt es auch hier Fallstricke. Man muss bei der Benennung und Implementierung aufmerksam sein und die Hackordnung der Methoden verstehen.

2 Stimmen

Man könnte argumentieren, dass statische Hilfsklassen in diesem speziellen Szenario genauso nützlich sind - und weit weniger gefährlich.

0 Stimmen

@Tobias Letztendlich ist es das, wozu es kompiliert wird, eine statische Klasse. Wenn eine Erweiterungsmethode und eine Instanzmethode den gleichen Namen haben, muss der Compiler eine Entscheidung treffen. Es gibt eine Eleganz bei Erweiterungsmethoden.

4voto

Jon Skeet Punkte 1325502

Ein Bruch, den wir gerade in der MehrLINQ Projekt: Wenn Sie eine generische Erweiterungsmethode schreiben, ist es unmöglich, sicherzustellen, dass sie mit allen Typen funktioniert. Wir haben eine Methode mit dieser Signatur:

public static IEnumerable<T> Concat<T>(this T head, IEnumerable<T> tail)

Sie können das nicht mit verwenden:

"foo".Concat(new [] { "tail" });

wegen der string.Concat Methode...

0 Stimmen

Oft ist es sogar besser, die Erweiterungsmethode statisch aufzurufen

2voto

Matt Grande Punkte 11622

Ich benutze Ruby on Rails schon fast so lange wie ich C# benutzt habe. Mit Ruby kann man etwas Ähnliches machen wie mit den neuen Erweiterungsmethoden. Es gibt natürlich potenzielle Probleme, wenn jemand die Methode gleich benennt, aber die Vorteile der Möglichkeit, einer geschlossenen Klasse Methoden hinzuzufügen, überwiegen bei weitem den potenziellen Nachteil (der wahrscheinlich durch schlechtes Design oder schlechte Planung verursacht würde).

2voto

Kit Punkte 16901

Um sicherzustellen, dass Erweiterungsmethoden nicht mit anderen Methoden (Erweiterungsmethoden oder anderen) kollidieren, können Sie unter anderem Folgendes verwenden FxCop mit Regeln wie Doppelte Signaturen von Erweiterungsmethoden verhindern .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X