Может ли анонимный класс C # реализовать интерфейс? - PullRequest
425 голосов
/ 10 октября 2008

Возможно ли, чтобы анонимный тип реализовывал интерфейс. У меня есть кусок кода, над которым я хотел бы поработать, но не знаю, как это сделать.

У меня была пара ответов, которые либо говорят «нет», либо создают класс, который реализует интерфейс, и создает новые экземпляры этого. Это не совсем идеально, но мне интересно, есть ли механизм для создания тонкого динамического класса поверх интерфейса, который сделал бы это простым.

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

Я нашел статью Динамическое перенос интерфейса , которая описывает один подход. Это лучший способ сделать это?

Ответы [ 8 ]

328 голосов
/ 10 октября 2008

Нет, анонимные типы не могут реализовать интерфейс. Из руководства по программированию C # :

Анонимные типы - это типы классов, состоящие из одного или нескольких общедоступных свойств, доступных только для чтения. Другие типы членов класса, такие как методы или события, не допускаются. Анонимный тип не может быть приведен к какому-либо интерфейсу или типу, кроме объекта.

84 голосов
/ 17 сентября 2010

Хотя это может быть двухлетний вопрос, и хотя ответы в теме все достаточно правдивы, я не могу удержаться от желания сказать вам, что на самом деле возможно иметь анонимный класс реализовать интерфейс, хотя для этого нужно немного творческого мошенничества.

В 2008 году я писал собственного провайдера LINQ для своего тогдашнего работодателя, и в какой-то момент мне нужно было иметь возможность отличать "мои" анонимные классы от других анонимных, что означало, что они реализуют интерфейс, который я мог бы использовать чтобы проверить их. Мы решили это путем использования аспектов (мы использовали PostSharp ), чтобы добавить реализацию интерфейса непосредственно в IL. Таким образом, на самом деле , позволяющее анонимным классам реализовывать интерфейсы, выполнимо , вам просто нужно немного изменить правила, чтобы попасть туда.

42 голосов
/ 03 ноября 2008

Приведение анонимных типов к интерфейсам было тем, о чем я давно хотел, но, к сожалению, текущая реализация вынуждает вас иметь реализацию этого интерфейса.

Лучшее решение - использовать динамический прокси-сервер, который создает реализацию для вас. Используя отличный проект LinFu вы можете заменить

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

с

 select new DynamicObject(new
 {
   A = value.A,
   B = value.C + "_" + value.D
 }).CreateDuck<DummyInterface>();
13 голосов
/ 09 ноября 2014

Анонимные типы могут реализовывать интерфейсы через динамический прокси.

Я написал метод расширения для GitHub и сообщение в блоге http://wblo.gs/feE для поддержки этого сценария.

Метод можно использовать так:

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; }
}
12 голосов
/ 10 октября 2008

Нет; анонимный тип не может делать ничего, кроме как иметь несколько свойств. Вам нужно будет создать свой собственный тип. Я не читал связанную статью подробно, но похоже, что она использует Reflection.Emit для создания новых типов на лету; но если вы ограничите обсуждение вещами внутри самого C # , вы не сможете делать то, что хотите.

11 голосов
/ 10 октября 2008

Лучшее решение - просто не использовать анонимные классы.

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

Обратите внимание, что вам нужно привести результат запроса к типу интерфейса. Возможно, есть лучший способ сделать это, но я не смог его найти.

7 голосов
/ 18 июля 2014

Ответ на специально заданный вопрос - нет. Но вы смотрели на насмешливые рамки? Я использую MOQ, но их миллионы, и они позволяют вам реализовать / заглушить (частично или полностью) встроенные интерфейсы. Например.

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);
}
0 голосов
/ 11 апреля 2019

Другой вариант - создать один конкретный реализующий класс, который принимает лямбда-выражения в конструкторе.

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

Если все, что вы когда-либо собираетесь сделать, это преобразовать DummySource в DummyInterface, тогда было бы проще иметь только один класс, который принимает DummySource в конструкторе и реализует интерфейс.

Но, если вам нужно преобразовать много типов в DummyInterface, это намного меньше, чем котельная плита.

...