Domanda

È possibile avere un tipo anonimo implementare un'interfaccia. Ho un pezzo di codice che mi piacerebbe lavorare, ma non so come farlo.

Ho avuto un paio di risposte che dicono di no o creano una classe che implementa l'interfaccia costruendone nuove istanze. Questo non è proprio l'ideale, ma mi chiedo se esiste un meccanismo per creare una classe dinamica sottile sopra un'interfaccia che lo renderebbe semplice.

public interface DummyInterface
{
    string A { get; }
    string B { get; }
}

public class DummySource
{
    public string A { get; set; }
    public string C { get; set; }
    public string D { get; set; }
}

public class Test
{
    public void WillThisWork()
    {
        var source = new DummySource[0];
        var values = from value in source
                     select new
                     {
                         A = value.A,
                         B = value.C + "_" + value.D
                     };

        DoSomethingWithDummyInterface(values);

    }

    public void DoSomethingWithDummyInterface(IEnumerable<DummyInterface> values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

Ho trovato un articolo Avvolgimento dinamico dell'interfaccia che descrive un approccio. È questo il modo migliore per farlo?

È stato utile?

Soluzione

No, i tipi anonimi non possono implementare un'interfaccia. Dalla Guida alla programmazione C # :

  

I tipi anonimi sono tipi di classe costituiti da una o più proprietà pubbliche di sola lettura. Non sono ammessi altri tipi di membri della classe come metodi o eventi. Un tipo anonimo non può essere trasmesso a nessuna interfaccia o tipo ad eccezione dell'oggetto.

Altri suggerimenti

Anche se questa potrebbe essere una domanda di due anni e mentre le risposte nel thread sono tutte abbastanza vere, non posso resistere alla tentazione di dirti che in effetti è possibile avere un anonimo classe implementa un'interfaccia, anche se ci vuole un po 'di imbroglio creativo per arrivarci.

Nel 2008 stavo scrivendo un fornitore LINQ personalizzato per il mio allora datore di lavoro, e ad un certo punto dovevo essere in grado di dire " il mio " classi anonime da altre anonime, il che significava che avrebbero implementato un'interfaccia che avrei potuto usare per digitare controllandoli. Il modo in cui l'abbiamo risolto è stato utilizzando gli aspetti (abbiamo usato PostSharp ), per aggiungere l'implementazione dell'interfaccia direttamente nell'IL. Quindi, in effetti, consentire alle classi anonime di implementare le interfacce è fattibile , devi solo piegare leggermente le regole per arrivarci.

Il cast di tipi anonimi su interfacce è qualcosa che volevo da un po ', ma sfortunatamente l'attuale implementazione ti costringe ad avere un'implementazione di quell'interfaccia.

La migliore soluzione è avere un tipo di proxy dinamico che crea l'implementazione per te. Utilizzando l'eccellente progetto LinFu puoi sostituire

select new
{
  A = value.A,
  B = value.C + "_" + value.D
};

con

 select new DynamicObject(new
 {
   A = value.A,
   B = value.C + "_" + value.D
 }).CreateDuck<DummyInterface>();

I tipi anonimi possono implementare interfacce tramite un proxy dinamico.

Ho scritto un metodo di estensione su GitHub e un post sul blog http://wblo.gs/feE per supportare questo scenario.

Il metodo può essere utilizzato in questo modo:

class Program
{
    static void Main(string[] args)
    {
        var developer = new { Name = "Jason Bowers" };

        PrintDeveloperName(developer.DuckCast<IDeveloper>());

        Console.ReadKey();
    }

    private static void PrintDeveloperName(IDeveloper developer)
    {
        Console.WriteLine(developer.Name);
    }
}

public interface IDeveloper
{
    string Name { get; }
}

No; un tipo anonimo non può essere fatto per fare nulla se non per avere alcune proprietà. Dovrai creare il tuo tipo. Non ho letto in dettaglio l'articolo collegato, ma sembra che usi Reflection.Emit per creare nuovi tipi al volo; ma se limiti la discussione alle cose all'interno di C # stesso non puoi fare quello che vuoi.

La soluzione migliore è semplicemente non usare classi anonime.

public class Test
{
    class DummyInterfaceImplementor : IDummyInterface
    {
        public string A { get; set; }
        public string B { get; set; }
    }

    public void WillThisWork()
    {
        var source = new DummySource[0];
        var values = from value in source
                     select new DummyInterfaceImplementor()
                     {
                         A = value.A,
                         B = value.C + "_" + value.D
                     };

        DoSomethingWithDummyInterface(values.Cast<IDummyInterface>());

    }

    public void DoSomethingWithDummyInterface(IEnumerable<IDummyInterface> values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

Nota che devi trasmettere il risultato della query al tipo di interfaccia. Potrebbe esserci un modo migliore per farlo, ma non sono riuscito a trovarlo.

La risposta alla domanda specificamente posta è no. Ma hai preso in considerazione quadri beffardi? Uso MOQ ma ce ne sono milioni là fuori e ti permettono di implementare / stub (parzialmente o completamente) interfacce in linea. Eg.

public void ThisWillWork()
{
    var source = new DummySource[0];
    var mock = new Mock<DummyInterface>();

    mock.SetupProperty(m => m.A, source.Select(s => s.A));
    mock.SetupProperty(m => m.B, source.Select(s => s.C + "_" + s.D));

    DoSomethingWithDummyInterface(mock.Object);
}

Un'altra opzione è quella di creare un'unica classe di implementazione concreta che prende lambda nel costruttore.

public interface DummyInterface
{
    string A { get; }
    string B { get; }
}

// "Generic" implementing class
public class Dummy : DummyInterface
{
    private readonly Func<string> _getA;
    private readonly Func<string> _getB;

    public Dummy(Func<string> getA, Func<string> getB)
    {
        _getA = getA;
        _getB = getB;
    }

    public string A => _getA();

    public string B => _getB();
}

public class DummySource
{
    public string A { get; set; }
    public string C { get; set; }
    public string D { get; set; }
}

public class Test
{
    public void WillThisWork()
    {
        var source = new DummySource[0];
        var values = from value in source
                     select new Dummy // Syntax changes slightly
                     (
                         getA: () => value.A,
                         getB: () => value.C + "_" + value.D
                     );

        DoSomethingWithDummyInterface(values);

    }

    public void DoSomethingWithDummyInterface(IEnumerable<DummyInterface> values)
    {
        foreach (var value in values)
        {
            Console.WriteLine("A = '{0}', B = '{1}'", value.A, value.B);
        }
    }
}

Se dovessi mai convertire DummySource in DummyInterface , sarebbe più semplice avere una sola classe che accetta un DummySource nel costruttore e implementa l'interfaccia.

Ma, se hai bisogno di convertire molti tipi in DummyInterface , questa è molto meno piastra della caldaia.

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