是否可以使用匿名类型实现接口。我有一段我想要工作的代码,但不知道该怎么做。
我有几个答案要么说不,要么创建一个实现接口的类构建新的实例。这不是很理想,但我想知道是否有一种机制可以在界面上创建一个瘦动态类,这样就可以实现这一点。
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。这是最好的方法吗?
答案 0 :(得分:328)
不,匿名类型无法实现接口。来自C# programming guide:
匿名类型是由一个或多个公共只读属性组成的类类型。不允许使用其他类别的成员,例如方法或事件。除了object之外,匿名类型不能强制转换为任何接口或类型。
答案 1 :(得分:84)
虽然这可能是一个两年前的问题,虽然线程中的答案都足够真实,但我无法抗拒告诉你实际上可能有匿名的冲动class实现了一个接口,即使它需要一点创造性的欺骗才能到达那里。
早在2008年,我正在为当时的雇主编写一个自定义LINQ提供程序,有一次我需要能够从其他匿名类中告诉“我的”匿名类,这意味着让他们实现一个我可以使用的界面键入检查它们。我们解决它的方法是使用方面(我们使用PostSharp),直接在IL中添加接口实现。所以,事实上,让匿名类实现接口是可行的,你只需稍微弯曲规则就可以实现。
答案 2 :(得分: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>();
答案 3 :(得分: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; }
}
答案 4 :(得分:12)
没有;除了具有一些属性之外,不能使匿名类型执行任何操作。您需要创建自己的类型。我没有深入阅读链接文章,但看起来它使用Reflection.Emit动态创建新类型;但如果你把讨论限制在C#本身的 中,你就无法做你想做的事。
答案 5 :(得分: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);
}
}
}
请注意,您需要将查询结果强制转换为接口类型。可能有更好的方法,但我找不到它。
答案 6 :(得分: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);
}
答案 7 :(得分: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
,则可以简化工作。
答案 8 :(得分:0)
从技术上讲,它不能完成定义,因为匿名类型仅包含公共属性。我不知道在哪种情况下需要实现接口,如果要正常创建的话。可能会有一些变通方法,例如创建委托,但您不应混淆用户。不要复杂化代码并使之保持简单。
答案 9 :(得分:0)
使用Roslyn,您可以动态创建一个从接口继承的类(或抽象类)。
我使用以下内容从抽象类创建具体的类。
在此示例中,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;
}
}
}