我想知道如何做这样的工作:
using System;
class Program
{
static void Main()
{
var f = new IFoo {
Foo = "foo",
Print = () => Console.WriteLine(Foo)
};
}
}
interface IFoo
{
String Foo { get; set; }
void Print();
}
创建的匿名类型看起来像这样:
internal sealed class <>f__AnonymousType0<<Foo>j__TPar> : IFoo
{
readonly <Foo>j__TPar <Foo>i__Field;
public <>f__AnonymousType0(<Foo>j__TPar Foo)
{
this.<Foo>i__Field = Foo;
}
public <Foo>j__TPar Foo
{
get { return this.<Foo>i__Field; }
}
public void Print()
{
Console.WriteLine(this.Foo);
}
}
有没有理由让编译器无法做到这样的事情?即使对于采用参数的非void方法或方法,编译器也应该能够从接口声明中推断出类型。
免责声明:虽然我确实认识到目前这不可行,但在这种情况下简单地创建一个具体类会更有意义,我对此理论方面更感兴趣。
答案 0 :(得分:9)
重载成员,索引器和显式接口实现会出现一些问题。
但是,您可以以允许您解决这些问题的方式定义语法。
有趣的是,通过编写库,您可以非常接近C#3.0所需的内容。基本上,你可以这样做:
Create<IFoo>
(
new
{
Foo = "foo",
Print = (Action)(() => Console.WriteLine(Foo))
}
);
这与你想要的非常接近。主要区别是调用“创建”而不是“新”关键字,以及需要指定委托类型的事实。
“创建”的声明如下:
T Create<T> (object o)
{
//...
}
然后,它将使用Reflection.Emit在运行时动态生成接口实现。
但是,这种语法在显式接口实现和重载成员方面确实存在问题,在不更改编译器的情况下无法解决这些问题。
另一种方法是使用集合初始值设定项而不是匿名类型。这看起来像这样:
Create
{
new Members<IFoo>
{
{"Print", ((IFoo @this)=>Console.WriteLine(Foo))},
{"Foo", "foo"}
}
}
这将使您能够:
你需要做一些事情来实现这个:
答案 1 :(得分:6)
它需要c#4,但开源框架impromptu interface可以在内部使用DLR代理来伪装这个开箱即用。虽然不如你提出的改变存在,但表现还是不错的。
using ImpromptuInterface.Dynamic;
...
var f = ImpromptuGet.Create<IFoo>(new{
Foo = "foo",
Print = ReturnVoid.Arguments(() => Console.WriteLine(Foo))
});
答案 2 :(得分:4)
除了具有只读属性外,不能使匿名类型执行任何操作。
引用C# Programming Guide (Anonymous Types):
“匿名类型是类类型 由一个或多个公众组成 只读属性。没有其他种类 类成员如方法或 事件是允许的。匿名类型 无法转换为任何界面或 除了对象之外的类型。“
答案 3 :(得分:2)
只要我们提出一个接口愿望清单,我真的希望能够告诉编译器一个类在类定义之外实现一个接口 - 即使是在一个单独的程序集中。
例如,假设我正在开发一个从不同存档格式中提取文件的程序。我希望能够从不同的库中提取现有的实现 - 比如,SharpZipLib和商业PGP实现 - 并使用相同的代码使用这两个库而无需创建新类。然后我可以在通用约束中使用来自任一源的类型,例如。
另一种用法是告诉编译器System.Xml.Serialization.XmlSerializer
实现了System.Runtime.Serialization.IFormatter
接口(它已经做了,但是编译器不知道它)。
这也可用于实现您的请求,而不是自动执行。您仍然必须明确告诉编译器。不确定语法的外观,因为你仍然需要在某处手动映射方法和属性,这意味着大量的措辞。也许类似于扩展方法。
答案 4 :(得分:1)
你可以在Java中使用anonymous classes:
using System;
class Program {
static void Main() {
var f = new IFoo() {
public String Foo { get { return "foo"; } }
public void Print() { Console.WriteLine(Foo); }
};
}
}
interface IFoo {
String Foo { get; set; }
void Print();
}
答案 5 :(得分:1)
这不是很酷。内联匿名类:
List<Student>.Distinct(new IEqualityComparer<Student>()
{
public override bool Equals(Student x, Student y)
{
return x.Id == y.Id;
}
public override int GetHashCode(Student obj)
{
return obj.Id.GetHashCode();
}
})
答案 6 :(得分:1)
我要在这里抛弃它。我刚才写了它,但是IIRC它运作正常。
首先是一个帮助函数,用于获取MethodInfo
并返回匹配Type
或Func
的{{1}}。不幸的是,你需要为每个参数提供一个分支,而且我显然已经停在了三个。
Action
现在是将接口作为泛型参数并返回实现接口的static Type GenerateFuncOrAction(MethodInfo method)
{
var typeParams = method.GetParameters().Select(p => p.ParameterType).ToArray();
if (method.ReturnType == typeof(void))
{
if (typeParams.Length == 0)
{
return typeof(Action);
}
else if (typeParams.Length == 1)
{
return typeof(Action<>).MakeGenericType(typeParams);
}
else if (typeParams.Length == 2)
{
return typeof(Action<,>).MakeGenericType(typeParams);
}
else if (typeParams.Length == 3)
{
return typeof(Action<,,>).MakeGenericType(typeParams);
}
throw new ArgumentException("Only written up to 3 type parameters");
}
else
{
if (typeParams.Length == 0)
{
return typeof(Func<>).MakeGenericType(typeParams.Concat(new[] { method.ReturnType }).ToArray());
}
else if (typeParams.Length == 1)
{
return typeof(Func<,>).MakeGenericType(typeParams.Concat(new[] { method.ReturnType }).ToArray());
}
else if (typeParams.Length == 2)
{
return typeof(Func<,,>).MakeGenericType(typeParams.Concat(new[] { method.ReturnType }).ToArray());
}
else if (typeParams.Length == 3)
{
return typeof(Func<,,,>).MakeGenericType(typeParams.Concat(new[] { method.ReturnType }).ToArray());
}
throw new ArgumentException("Only written up to 3 type parameters");
}
}
并且具有构造函数(需要通过Type
调用)并取Activator.CreateInstance
的方法或每个方法/ getter / setter的Func
。但是,您需要知道将它们放在构造函数中的正确顺序。或者(注释掉的代码)它可以生成一个DLL,然后您可以引用它并直接使用该类型。
Action
您可以将其用作例如。
static Type GenerateInterfaceImplementation<TInterface>()
{
var interfaceType = typeof(TInterface);
var funcTypes = interfaceType.GetMethods().Select(GenerateFuncOrAction).ToArray();
AssemblyName aName =
new AssemblyName("Dynamic" + interfaceType.Name + "WrapperAssembly");
var assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
aName,
AssemblyBuilderAccess.Run/*AndSave*/); // to get a DLL
var modBuilder = assBuilder.DefineDynamicModule(aName.Name/*, aName.Name + ".dll"*/); // to get a DLL
TypeBuilder typeBuilder = modBuilder.DefineType(
"Dynamic" + interfaceType.Name + "Wrapper",
TypeAttributes.Public);
// Define a constructor taking the same parameters as this method.
var ctrBuilder = typeBuilder.DefineConstructor(
MethodAttributes.Public | MethodAttributes.HideBySig |
MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
CallingConventions.Standard,
funcTypes);
// Start building the constructor.
var ctrGenerator = ctrBuilder.GetILGenerator();
ctrGenerator.Emit(OpCodes.Ldarg_0);
ctrGenerator.Emit(
OpCodes.Call,
typeof(object).GetConstructor(Type.EmptyTypes));
// For each interface method, we add a field to hold the supplied
// delegate, code to store it in the constructor, and an
// implementation that calls the delegate.
byte methodIndex = 0;
foreach (var interfaceMethod in interfaceType.GetMethods())
{
ctrBuilder.DefineParameter(
methodIndex + 1,
ParameterAttributes.None,
"del_" + interfaceMethod.Name);
var delegateField = typeBuilder.DefineField(
"del_" + interfaceMethod.Name,
funcTypes[methodIndex],
FieldAttributes.Private);
ctrGenerator.Emit(OpCodes.Ldarg_0);
ctrGenerator.Emit(OpCodes.Ldarg_S, methodIndex + 1);
ctrGenerator.Emit(OpCodes.Stfld, delegateField);
var metBuilder = typeBuilder.DefineMethod(
interfaceMethod.Name,
MethodAttributes.Public | MethodAttributes.Virtual |
MethodAttributes.Final | MethodAttributes.HideBySig |
MethodAttributes.NewSlot,
interfaceMethod.ReturnType,
interfaceMethod.GetParameters()
.Select(p => p.ParameterType).ToArray());
var metGenerator = metBuilder.GetILGenerator();
metGenerator.Emit(OpCodes.Ldarg_0);
metGenerator.Emit(OpCodes.Ldfld, delegateField);
// Generate code to load each parameter.
byte paramIndex = 1;
foreach (var param in interfaceMethod.GetParameters())
{
metGenerator.Emit(OpCodes.Ldarg_S, paramIndex);
paramIndex++;
}
metGenerator.EmitCall(
OpCodes.Callvirt,
funcTypes[methodIndex].GetMethod("Invoke"),
null);
metGenerator.Emit(OpCodes.Ret);
methodIndex++;
}
ctrGenerator.Emit(OpCodes.Ret);
// Add interface implementation and finish creating.
typeBuilder.AddInterfaceImplementation(interfaceType);
var wrapperType = typeBuilder.CreateType();
//assBuilder.Save(aName.Name + ".dll"); // to get a DLL
return wrapperType;
}
答案 7 :(得分:0)
有趣的想法,我有点担心,即使可以做到,也可能会让人感到困惑。例如。在使用非平凡的setter和getter定义属性时,或者如果声明类型还包含一个名为Foo的属性,如何消除Foo的歧义。
我想知道在更动态的语言中,或者使用C#4.0中的动态类型和DLR,这会更容易吗?
也许今天在C#中,一些意图可以通过lambdas来实现:
void Main() {
var foo = new Foo();
foo.Bar = "bar";
foo.Print = () => Console.WriteLine(foo.Bar);
foo.Print();
}
class Foo : IFoo {
public String Bar { get; set; }
public Action Print {get;set;}
}
答案 8 :(得分:-1)
目前这是不可能的。
这有什么区别,只是让IFoo成为一个具体的类?似乎这可能是更好的选择。
需要什么?一个新的编译器和大量的检查,以确保它们没有打破其他功能。就个人而言,我认为要求开发人员创建他们类的具体版本会更容易。
答案 9 :(得分:-1)
我通过“new IFoo(){...}”sintax在Java中使用了Amonimous类,当你必须快速实现一个简单的接口时,它很实用。
作为一个示例,以这种方式在传统对象上实现IDisposable会很好,只需一次,而不是派生一个新类来实现它。