匿名类可以实现接口吗?


462

是否可以使用匿名类型实现接口?

我有一段我想工作的代码,但是不知道该怎么做。

我有几个答案,要么说不,要么创建一个实现接口的类,以构造新的实例。这并不是很理想,但是我想知道是否有一种机制可以在接口顶部创建一个精简的动态类,从而简化这一过程。

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

我发现了一篇文章动态接口包装描述了一种方法。这是最好的方法吗?


1
链接似乎已过期,这也许是一个合适的替代liensberger.it/web/blog/?p=298
Phil Cooper

1
是的,您可以使用ImpromptuInterface nuget包在.NET 4及更高版本(通过DLR)中进行此操作。
BrainSlugs83 2013年

1
@PhilCooper您的链接已关闭,可能至少是从2016年开始-幸运的是,此链接在此之前被存档。web.archive.org/web/20111105150920/http://www.liensberger.it/...
保罗

Answers:


360

不可以,匿名类型不能实现接口。从C#编程指南中

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


5
反正拥有这些东西会很好。如果您在谈论代码的可读性,那么lambda表达式通常不是可行的方法。如果我们在谈论RAD,那么我将全神贯注于类似Java的匿名接口实现。顺便说一句,在某些情况下,该功能比代表更强大
Arsen Zahray 2012年

18
@ArsenZahray:lambda表达式使用得当,实际上可以提高代码的可读性。当用于功能链时,它们特别强大,可以减少或消除对局部变量的需求。
罗伊·廷克

2
您可以通过“匿名实现类– C#的设计模式”的方式来达到目的 -twistedoakstudios.com/blog/…–
Dmitry Pavlov

3
@DmitryPavlov,这是非常有价值的。路人: 是精简版。
kdbanman

1
您可以将匿名类型转换为具有相同字段stackoverflow.com/questions/1409734/cast-to-anonymous-type
Zinov,2016年

89

尽管这可能是一个存在两年的问题,并且尽管线程中的答案都足够真实,但我无法抗拒告诉您实际上有可能使用匿名类实现接口,即使它需要一个接口。有点创意作弊才能到达那里。

早在2008年,我正在为当时的雇主编写一个自定义LINQ提供程序,并且在某一点上,我需要能够将其他匿名类中的“我的”匿名类告诉出来,这意味着让它们实现一个我可以用来键入检查的接口。他们。解决问题的方法是使用多方面(我们使用PostSharp),直接在IL中添加接口实现。因此,实际上,让匿名类实现接口是可行的,您只需要稍微改变规则即可。


8
@Gusdor,在这种情况下,我们可以完全控制构建,并且始终在专用计算机上运行。而且,由于我们使用的是PostSharp,并且在该框架内我们的工作是完全合法的,因此只要确保将PostSharp安装在所使用的构建服务器上,任何事情就不会真正流行。
Mia Clarke 2012年

16
@Gusdor我同意其他程序员应该很容易就可以轻松获得项目并进行编译,而不会遇到很多困难,但这是一个可以分别解决的不同问题,而无需完全避免使用诸如postharp之类的工具或框架。您可以针对VS本身或不属于C#规范的任何其他非标准MS框架提出相同的论点。您需要这些东西,否则它会“弹出”。IMO没问题。问题是,当构建变得如此复杂以至于很难使所有这些东西一起正常工作时。
AaronLS 2012年

6
@ZainRizvi不,不是。据我所知,它仍在生产中。那时引起的关注对我来说似乎很奇怪,现在对我而言充其量是任意的。基本上是说“不使用框架,事情就会崩溃!”。他们没有,什么也没有流行,我并不感到惊讶。
米娅·克拉克

3
现在要容易得多;生成代码后无需修改IL;只需使用ImpromptuInterface即可。-它使您可以将任何对象(包括匿名键入的对象)绑定到任何接口(如果您尝试使用该类实际上不支持的接口的一部分,当然会有后期绑定异常)。
BrainSlugs83 2013年

44

我一直想要将匿名类型转换为接口,但是不幸的是,当前的实现迫使您必须实现该接口的实现。

围绕它的最佳解决方案是使用某种类型的动态代理为您创建实现。使用出色的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>();

17
Impromptu接口项目将使用DLR在.NET 4.0中执行此操作,并且比Linfu轻巧。
jbtule'3

DynamicObject林富型吗?System.Dynamic.DynamicObject仅具有受保护的构造函数(至少在.NET 4.5中)。
jdmcnair 2014年

是。我指的是LinFu实现,DynamicObject该实现早于DLR版本
Arne Claassen 2014年

14

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

我在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; }
}

13

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


需要特别注意的是:属性也可以包含函数或void(动作):选择new {... MyFunction = new Func <string,bool>(s => value.A == s)}可以工作,但您不能引用函数中的新属性(我们不能使用“ A”代替“ value.A”)。
cfeduke

2
好吧,这不只是一个碰巧是委托的财产吗?它实际上不是方法。
马克·格雷韦尔

1
我使用了Reflection.Emit在运行时创建类型,但相信现在我希望使用AOP解决方案来避免运行时成本。
诺曼H

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

请注意,您需要将查询结果转换为接口的类型。也许有更好的方法可以做到,但我找不到。


2
您可以使用values.OfType<IDummyInterface>()而不是强制转换。它仅返回集合中实际上可以转换为该类型的对象。这完全取决于您想要什么。
克里斯托弗L

7

具体问题的答案是否定的。但是,您是否一直在研究模拟框架?我使用最小起订量,但是有上百万个起订量,它们使您可以在线实现(部分或全部)接口。例如。

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

如果您要做的只是将转换DummySourceDummyInterface,那么只拥有一个DummySource在构造函数中采用并实现接口的类会更简单。

但是,如果您需要将多种类型转换为DummyInterface,则锅炉板要少得多。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.