匿名类能否实现接口?

Can a C# anonymous class implement an interface?

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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);
        }
    }
}

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


不,匿名类型不能实现接口。从C编程指南:

Anonymous types are class types that consist of one or more public read-only properties. No other kinds of class members such as methods or events are allowed. An anonymous type cannot be cast to any interface or type except for object.


虽然这可能是一个2年前的问题,虽然线程中的答案都是正确的,但我还是忍不住要告诉您,事实上,匿名类实现接口是可能的,尽管实现接口需要一些创造性的欺骗。

早在2008年,我就为当时的雇主编写了一个定制的LINQ提供者,在某一点上,我需要能够将"我的"匿名类与其他匿名类区分开来,这意味着让它们实现一个接口,我可以使用该接口对它们进行类型检查。我们解决这个问题的方法是使用方面(我们使用Postshap),直接在IL中添加接口实现。因此,实际上,让匿名类实现接口是可行的,您只需要稍微弯曲规则就可以实现。


将匿名类型强制转换为接口已经有一段时间了,但不幸的是,当前的实现强制您实现该接口。

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

1
2
3
4
5
select new
{
  A = value.A,
  B = value.C +"_" + value.D
};

具有

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


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

我在Github上写了一个扩展方法和一篇博客文章http://wblo.gs/fee来支持这个场景。

方法可以这样使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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; }
}

不;除了具有一些属性外,不能使匿名类型执行任何操作。您需要创建自己的类型。我没有深入阅读链接的文章,但它看起来像是使用了反射。Emit可以快速创建新类型;但是如果您将讨论限制在C本身的范围内,您就不能做您想做的事情。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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);
        }
    }
}

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


具体问题的答案是否定的,但是你是否在研究模拟框架?我使用moq,但是有数百万个,它们允许您实现/存根(部分或全部)在线接口。如。

1
2
3
4
5
6
7
8
9
10
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);
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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,那么这就少了很多锅炉板。