C#匿名类可以实现一个接口吗?

问题描述 投票:420回答:8

是否可以使用匿名类型实现接口。我有一段我想要工作的代码,但不知道该怎么做。

我有几个答案要么说不,要么创建一个实现接口的类构造新的实例。这不是很理想,但我想知道是否有一种机制可以在界面上创建一个瘦动态类,这将使这个变得简单。

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

我发现了一篇描述一种方法的文章Dynamic interface wrapping。这是最好的方法吗?

c# anonymous-types
8个回答
331
投票

不,匿名类型无法实现接口。来自C# programming guide

匿名类型是由一个或多个公共只读属性组成的类类型。不允许使用其他类别的成员,例如方法或事件。除对象外,匿名类型不能强制转换为任何接口或类型。


86
投票

虽然这可能是一个两年前的问题,虽然线程中的答案都足够真实,但我无法抗拒告诉你实际上有可能有一个匿名类实现一个接口的冲动,即使它需要一个有点创意作弊去那儿。

早在2008年,我正在为我当时的雇主编写一个自定义LINQ提供程序,有一次我需要能够从其他匿名类中告诉“我的”匿名类,这意味着让他们实现一个我可以用来键入check的接口他们。我们解决它的方法是使用方面(我们使用PostSharp),直接在IL中添加接口实现。所以,事实上,让匿名类实现接口是可行的,你只需稍微弯曲规则就可以实现。


42
投票

将匿名类型转换为接口已经是我想要的一段时间,但不幸的是,当前的实现迫使您拥有该接口的实现。

围绕它的最佳解决方案是使用某种类型的动态代理来为您创建实现。使用优秀的LinFu project,你可以取代

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
投票

匿名类型可以通过动态代理实现接口。

我在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
投票

没有;除了具有一些属性之外,不能使匿名类型执行任何操作。您需要创建自己的类型。我没有深入阅读链接文章,但看起来它使用Reflection.Emit动态创建新类型;但是如果你把讨论限制在C#内部的事物上,你就无法做你想做的事情。


11
投票

最好的解决方案就是不要使用匿名类。

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
投票

具体问到的问题的答案是否定的。但你有没有看过嘲笑框架?我使用MOQ,但那里有数以百万计的MOQ,它们允许你在线实现/ stub(部分或全部)接口。例如。

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
投票

另一种选择是创建一个在构造函数中使用lambda的单个具体实现类。

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,那么锅炉板就更少了。

© www.soinside.com 2019 - 2024. All rights reserved.