匿名类可以实现接口吗?

问题描述 投票:0回答:10

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

我有一段代码想要工作,但不知道如何执行。

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

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
10个回答
392
投票

不,匿名类型不能实现接口。来自 C# 编程指南

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


98
投票

虽然线程中的答案都足够真实,但我无法抗拒告诉您事实上“有可能”让匿名类实现接口,尽管需要一些创造性的作弊才能实现这一点。 早在 2008 年,我正在为当时的雇主编写一个自定义 LINQ 提供程序,有一次我需要能够区分“我的”匿名类和其他匿名类,这意味着让它们实现一个我可以用来键入的接口检查它们。我们解决这个问题的方法是使用方面(我们使用

PostSharp

),直接在IL中添加接口实现。所以,事实上,让匿名类实现接口是可行的,你只需要稍微改变规则即可实现。


49
投票

最好的解决方案是使用某种类型的动态代理来为您创建实现。使用优秀的

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

我在

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
投票
C# 本身内部

,你就无法做你想做的事。


10
投票

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

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


9
投票

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



2
投票

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

,那么这就不那么繁琐了。

    


0
投票

我使用以下方法从抽象类创建具体类。

在此示例中,AAnimal 是一个抽象类。

var personClass = typeof(AAnimal).CreateSubclass("Person");

然后就可以实例化一些对象了:

var person1 = Activator.CreateInstance(personClass); var person2 = Activator.CreateInstance(personClass);

毫无疑问,这并不适用于所有情况,但它应该足以让您开始:

using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; namespace Publisher { public static class Extensions { public static Type CreateSubclass(this Type baseType, string newClassName, string newNamespace = "Magic") { //todo: handle ref, out etc. var concreteMethods = baseType .GetMethods() .Where(method => method.IsAbstract) .Select(method => { var parameters = method .GetParameters() .Select(param => $"{param.ParameterType.FullName} {param.Name}") .ToString(", "); var returnTypeStr = method.ReturnParameter.ParameterType.Name; if (returnTypeStr.Equals("Void")) returnTypeStr = "void"; var methodString = @$" public override {returnTypeStr} {method.Name}({parameters}) {{ Console.WriteLine(""{newNamespace}.{newClassName}.{method.Name}() was called""); }}"; return methodString.Trim(); }) .ToList(); var concreteMethodsString = concreteMethods .ToString(Environment.NewLine + Environment.NewLine); var classCode = @$" using System; namespace {newNamespace} {{ public class {newClassName}: {baseType.FullName} {{ public {newClassName}() {{ }} {concreteMethodsString} }} }} ".Trim(); classCode = FormatUsingRoslyn(classCode); /* var assemblies = new[] { MetadataReference.CreateFromFile(typeof(object).Assembly.Location), MetadataReference.CreateFromFile(baseType.Assembly.Location), }; */ var assemblies = AppDomain .CurrentDomain .GetAssemblies() .Where(a => !string.IsNullOrEmpty(a.Location)) .Select(a => MetadataReference.CreateFromFile(a.Location)) .ToArray(); var syntaxTree = CSharpSyntaxTree.ParseText(classCode); var compilation = CSharpCompilation .Create(newNamespace) .AddSyntaxTrees(syntaxTree) .AddReferences(assemblies) .WithOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)); using (var ms = new MemoryStream()) { var result = compilation.Emit(ms); //compilation.Emit($"C:\\Temp\\{newNamespace}.dll"); if (result.Success) { ms.Seek(0, SeekOrigin.Begin); Assembly assembly = Assembly.Load(ms.ToArray()); var newTypeFullName = $"{newNamespace}.{newClassName}"; var type = assembly.GetType(newTypeFullName); return type; } else { IEnumerable<Diagnostic> failures = result.Diagnostics.Where(diagnostic => diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error); foreach (Diagnostic diagnostic in failures) { Console.Error.WriteLine("{0}: {1}", diagnostic.Id, diagnostic.GetMessage()); } return null; } } } public static string ToString(this IEnumerable<string> list, string separator) { string result = string.Join(separator, list); return result; } public static string FormatUsingRoslyn(string csCode) { var tree = CSharpSyntaxTree.ParseText(csCode); var root = tree.GetRoot().NormalizeWhitespace(); var result = root.ToFullString(); return result; } } }



0
投票

您的情况:

public class Test { public void WillThisWork() { var source = new DummySource[0]; var values = from value in source select new DummyImplementation(value.A, 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); } } private record DummyImplementation(string A, string B) : DummyInterface { } }

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