Classe anônima pode implementar interface?

463

É possível que um tipo anônimo implemente uma interface?

Eu tenho um pedaço de código que gostaria de trabalhar, mas não sei como fazer isso.

Eu tive algumas respostas que dizem não ou criam uma classe que implementa a interface para construir novas instâncias disso. Isso não é realmente ideal, mas estou me perguntando se existe um mecanismo para criar uma classe dinâmica fina em cima de uma interface que tornaria isso simples.

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

Encontrei um artigo Encapsulamento de interface dinâmica que descreve uma abordagem. Essa é a melhor maneira de fazer isso?

Nick Randell
fonte
1
O link parece desatualizado, talvez seja uma alternativa adequada liensberger.it/web/blog/?p=298 .
Phil Cooper
1
Sim, você pode fazer isso com o .NET 4 e superior (via DLR), usando o pacote de nuget ImpromptuInterface .
precisa saber é o seguinte
1
@ PhilCooper Seu link está inoperante, provavelmente desde pelo menos 2016 - mas, felizmente, ele foi arquivado antes disso. web.archive.org/web/20111105150920/http://www.liensberger.it/…
Paul

Respostas:

361

Não, tipos anônimos não podem implementar uma interface. No guia de programação em C # :

Tipos anônimos são tipos de classe que consistem em uma ou mais propriedades públicas somente leitura. Nenhum outro tipo de membro da classe, como métodos ou eventos, é permitido. Um tipo anônimo não pode ser convertido para nenhuma interface ou tipo, exceto para o objeto.

HasaniH
fonte
5
seria bom ter essas coisas de qualquer maneira. Se você está falando de legibilidade de código, as expressões lambda geralmente não são o caminho a percorrer. Se estamos falando de RAD, eu gosto de implementação de interface anônima semelhante a java. By the way, em alguns casos, esse recurso é mais poderoso do que os delegados
Arsen Zahray
18
@ArsenZahray: as expressões lambda, quando bem utilizadas, aumentam a legibilidade do código. Eles são particularmente poderosos quando usados ​​em cadeias funcionais, o que pode reduzir ou eliminar a necessidade de variáveis ​​locais.
Roy Tinker
2
Você poderia fazer o truque desta maneira "Classes de implementação anônimas - um padrão de design para C #" - twistedoakstudios.com/blog/…
Dmitry Pavlov
3
@DmitryPavlov, isso foi surpreendentemente valioso. Transeuntes: aqui está a versão condensada.
Kdbanman # /
1
você pode converter o tipo anônimo para um objeto anônimo com os mesmos campos stackoverflow.com/questions/1409734/cast-to-anonymous-type
Zinov
89

Embora essa possa ser uma pergunta de dois anos e, embora as respostas no tópico sejam todas verdadeiras, não resisto à tentação de dizer que é realmente possível que uma classe anônima implemente uma interface, mesmo que seja necessário um pouco de trapaça criativa para chegar lá.

Em 2008, eu estava escrevendo um provedor LINQ personalizado para o meu então empregador e, em um determinado momento, eu precisava ser capaz de diferenciar "minhas" classes anônimas de outras classes anônimas, o que significava que elas implementassem uma interface que eu poderia usar para digitar check eles. A maneira como resolvemos isso foi usando aspectos (usamos PostSharp ), para adicionar a implementação da interface diretamente na IL. Então, de fato, é possível permitir que classes anônimas implementem interfaces , você só precisa dobrar as regras um pouco para chegar lá.

Mia Clarke
fonte
8
@Gusdor, nesse caso, tínhamos controle total sobre a construção, e ela sempre era executada em uma máquina dedicada. Além disso, como estávamos usando o PostSharp, e o que estávamos fazendo é totalmente legal nessa estrutura, nada poderia aparecer desde que tivéssemos certeza de que o PostSharp estava instalado no servidor de construção que estávamos usando.
Mia Clarke
16
@Gusdor Concordo que deve ser fácil para outros programadores obter o projeto e compilar sem grande dificuldade, mas esse é um problema diferente que pode ser tratado separadamente, sem evitar completamente ferramentas ou estruturas como postsharp. O mesmo argumento que você cria pode ser feito contra o próprio VS ou qualquer outra estrutura MS não padrão que não faça parte da especificação C #. Você precisa dessas coisas ou então "vai pop". Isso não é um problema IMO. O problema é quando a compilação se torna tão complicada que é difícil fazer com que tudo funcione bem.
AaronLS
6
@ZainRizvi Não, não foi. Até onde eu sei, ainda está em produção. A preocupação levantada me parecia estranha na época e arbitrária na melhor das hipóteses para mim agora. Dizia basicamente "Não use uma estrutura, as coisas vão quebrar!". Eles não fizeram, nada apareceu e eu não estou surpreso.
Mia Clarke
3
É muito mais fácil agora; não há necessidade de modificar a IL após a geração do código; basta usar ImpromptuInterface . - Permite vincular qualquer objeto (incluindo objetos digitados anonimamente) a qualquer interface (é claro que haverá exceções de ligação tardia se você tentar usar uma parte da interface que a classe realmente não suporta).
precisa saber é o seguinte
44

A conversão de tipos anônimos para interfaces é algo que eu queria há algum tempo, mas infelizmente a implementação atual obriga a uma implementação dessa interface.

A melhor solução para isso é ter algum tipo de proxy dinâmico que crie a implementação para você. Usando o excelente projeto LinFu, você pode substituir

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

com

 select new DynamicObject(new
 {
   A = value.A,
   B = value.C + "_" + value.D
 }).CreateDuck<DummyInterface>();
Arne Claassen
fonte
17
O Projeto Impromptu-Interface fará isso no .NET 4.0 usando o DLR e é mais leve que o Linfu.
Jbtule #
É do DynamicObjecttipo LinFu? System.Dynamic.DynamicObjectpossui apenas um construtor protegido (pelo menos no .NET 4.5).
Jdmcnair 11/09/14
Sim. Eu estava me referindo à implementação LinFu de DynamicObjectque antecede a versão DLR
Arne Claassen
15

Tipos anônimos podem implementar interfaces por meio de um proxy dinâmico.

Eu escrevi um método de extensão no GitHub e uma postagem no blog http://wblo.gs/feE para suportar esse cenário.

O método pode ser usado assim:

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; }
}
Jason Bowers
fonte
13

Não; um tipo anônimo não pode ser feito para fazer nada, exceto ter algumas propriedades. Você precisará criar seu próprio tipo. Não li o artigo vinculado em profundidade, mas parece que ele usa o Reflection.Emit para criar novos tipos em tempo real; mas se você limitar a discussão a coisas no próprio C #, não poderá fazer o que deseja.

Marc Gravell
fonte
E importante observar: as propriedades também podem incluir funções ou espaços em branco (Ação): selecione new {... MyFunction = new Func <string, bool> (s => value.A == s)} funciona, embora você não possa se referir a novas propriedades em suas funções (não podemos usar "A" no lugar de "value.A").
cfeduke 10/10/08
2
Bem, isso não é apenas uma propriedade que passa a ser um delegado? Na verdade, não é um método.
Marc Gravell
1
Eu usei o Reflection.Emit para criar tipos em tempo de execução, mas acredito que agora eu preferiria uma solução de AOP para evitar os custos de tempo de execução.
Norman H
11

A melhor solução é simplesmente não usar classes anônimas.

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

Observe que você precisa converter o resultado da consulta no tipo de interface. Pode haver uma maneira melhor de fazê-lo, mas não consegui encontrá-lo.

ICR
fonte
2
Você poderia usar em values.OfType<IDummyInterface>()vez de transmitir. Ele retorna apenas os objetos em sua coleção que realmente podem ser convertidos para esse tipo. Tudo depende do que você quer.
precisa
7

A resposta para a pergunta especificamente feita é não. Mas você está olhando estruturas de zombaria? Uso MOQ, mas existem milhões por aí e eles permitem que você implemente / stub (parcialmente ou totalmente) interfaces em linha. Por exemplo.

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);
}
Nove Caldas
fonte
1

Outra opção é criar uma classe de implementação única e concreta que aceite lambdas no construtor.

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 tudo o que você vai fazer é converter DummySourcepara DummyInterface, seria mais simples ter apenas uma classe que pega um DummySourceno construtor e implementa a interface.

Mas, se você precisar converter vários tipos para DummyInterface, isso é muito menos placa de caldeira.

Gordon Bean
fonte