Domanda

Attualmente stiamo discutendo se i metodi di estensione in .NET sono cattivi o meno. O in quali circostanze i metodi di estensione possono presentare bug difficili da trovare o comportarsi in modo imprevisto.

Ci siamo inventati:

  • Scrivere un metodo di estensione per tipi che non sono sotto il tuo controllo (ad esempio estendere DirectoryInfo con GetTotalSize (), ecc ...) non è corretto, perché il proprietario dell'API potrebbe introdurre un metodo che nasconde la nostra estensione - e potrebbe avere diversi casi limite. Ad esempio, il test di null in un metodo di estensione si tradurrà automaticamente in NullReferenceException se il metodo di estensione non viene più utilizzato a causa del nascondiglio.

Domanda:

  • Ci sono altre situazioni pericolose oltre a " nascondere " a cui non stiamo pensando?

Modifica:

Un'altra situazione molto pericolosa. Supponiamo di avere un metodo di estensione:

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

E usalo:

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));
        }
    }
}

Nota che lo spazio dei nomi in cui lo usi è più lungo.

Ora fai riferimento a una dll con questo:

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

E il tuo test fallirà! Si compilerà senza un errore del compilatore. fallirà semplicemente . Senza nemmeno dover specificare & Quot; usando Example.ExtensionMethods.Conflict & Quot ;. Il compilatore percorrerà il nome dello spazio dei nomi e troverà Example.ExtensionMethods.Conflict.ConflictExtension prima di Example.ExtensionMethods.Extension e utilizzerà tale senza mai lamentarsi di metodi di estensione ambigui . Oh l'orrore!

È stato utile?

Soluzione

Alcune curiosità:

  • i metodi di estensione potrebbero essere chiamati su null istanze; questo potrebbe essere fonte di confusione (ma a volte utile)
  • il " nascondendo " problema è un grosso problema se hanno intenti diversi
  • allo stesso modo, potresti ottenere un metodo di estensione diverso con lo stesso nome da 2 spazi dei nomi diversi; se hai solo uno dei due spazi dei nomi, questo potrebbe portare a comportamenti incoerenti (a seconda di quale) ...
  • ... ma se qualcuno aggiunge un metodo di estensione simile (stessa firma) in un secondo spazio dei nomi utilizzato dal codice, si interromperà al momento della compilazione (ambiguo)

( modifica ) E, naturalmente, c'è il " Nullable<T> / new() " bomb ( vedi qui ) ...

Altri suggerimenti

Non sono d'accordo, il punto centrale dei metodi di estensione è aggiungere i tuoi membri alle classi in black box. Come ogni altra cosa che ci sono insidie, devi essere attento nel nominare, implementare e comprendere l'ordine di beccata dei metodi.

Una rottura che abbiamo appena trovato nel progetto MoreLINQ : se scrivi un metodo di estensione generico, è impossibile assicurarsi che funzionerà con tutti i tipi. Abbiamo un metodo con questa firma:

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

Non puoi usarlo con:

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

a causa del string.Concat metodo ...

Ho usato Ruby on Rails per quasi il tempo in cui ho usato C #. Ruby ti consente di fare qualcosa di simile ai nuovi metodi di estensione. Ci sono, naturalmente, potenziali problemi se qualcuno chiama lo stesso metodo, ma i vantaggi di poter aggiungere metodi a una classe chiusa superano di gran lunga il potenziale svantaggio (che probabilmente sarebbe causato da una cattiva progettazione o da una cattiva pianificazione).

Una cosa che puoi fare per assicurarti che i metodi di estensione non siano in conflitto con altri metodi (estensione o altro) è usare FxCop con regole come Impedisci duplicati del metodo di estensione .

Prima di tutto credo che la tua formulazione sia un po 'fuorviante. Presumo che tu stia parlando di & Quot; tipi & Quot; e non " oggetti " ;.

In secondo luogo, il grande vantaggio dei metodi di estensione è che puoi aggiungere funzionalità per digitare che non controlli. Se controlli il tipo, perché non modificare semplicemente il tipo invece di fare affidamento sui metodi di estensione?

Abbiamo preso l'atteggiamento nella mia squadra secondo cui i metodi di estensione sono così utili che non puoi realisticamente vietarli, ma così pericolosi (principalmente a causa del problema dei nascondigli) che devi essere un po 'cauto. Quindi abbiamo deciso che tutti i nomi dei metodi di estensione devono essere preceduti da X (quindi abbiamo un sacco di XInit...() metodi per inizializzare i controlli in modo utile, per esempio). In questo modo a) la probabilità di una collisione di denominazione è ridotta eb) il programmatore sa che sta usando un metodo di estensione e non un metodo di classe.

Quali sono i metodi di estensione delle chiamate .Net sono anche una forma limitata MonkeyPatching ( prova a ignorare il php rant lì dentro).

Questo dovrebbe darti del materiale per la tua discussione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top