C#功能请求:在匿名类型上实现接口

时间:2022-09-02 09:29:11

I am wondering what it would take to make something like this work:

我想知道做这样的工作需要什么:

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

The anonymous type created would look something like this:

创建的匿名类型看起来像这样:

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

Is there any reason that the compiler would be unable to do something like this? Even for non-void methods or methods that take parameters the compiler should be able to infer the types from the interface declaration.

是否有任何理由让编译器无法做到这样的事情?即使对于采用参数的非void方法或方法,编译器也应该能够从接口声明中推断出类型。

Disclaimer: While I do realize that this is not currently possible and it would make more sense to simply create a concrete class in this instance I am more interested in the theoretical aspects of this.

免责声明:虽然我确实认识到目前这不可行,但在这种情况下简单地创建一个具体的类更有意义,我对此的理论方面更感兴趣。

10 个解决方案

#1


There would be a few issues with overloaded members, indexers, and explicit interface implementations.

重载成员,索引器和显式接口实现会有一些问题。

However, you could probably define the syntax in a way that allows you to resolve those problems.

但是,您可以以允许您解决这些问题的方式定义语法。

Interestingly, you can get pretty close to what you want with C# 3.0 by writing a library. Basically, you could do this:

有趣的是,通过编写库,您可以非常接近C#3.0所需的内容。基本上,你可以这样做:

Create<IFoo>
(
    new
    {
        Foo = "foo",
        Print = (Action)(() => Console.WriteLine(Foo))
    }
);

Which is pretty close to what you want. The primary differences are a call to "Create" instead of the "new" keyword and the fact that you need to specify a delegate type.

这与你想要的非常接近。主要区别是调用“Create”而不是“new”关键字,以及需要指定委托类型的事实。

The declaration of "Create" would look like this:

“创建”的声明如下所示:

T Create<T> (object o)
{
//...
}

It would then use Reflection.Emit to generate an interface implementation dynamically at runtime.

然后,它将使用Reflection.Emit在运行时动态生成接口实现。

This syntax, however, does have problems with explicit interface implementations and overloaded members, that you couldn't resolve without changing the compiler.

但是,这种语法确实存在显式接口实现和重载成员的问题,如果不更改编译器就无法解决。

An alternative would be to use a collection initializer rather than an anonymous type. That would look like this:

另一种方法是使用集合初始化程序而不是匿名类型。这看起来像这样:

Create
{
    new Members<IFoo>
    {
        {"Print", ((IFoo @this)=>Console.WriteLine(Foo))},
        {"Foo", "foo"}
    }
}

That would enable you to:

这将使您能够:

  1. Handle explicit interface implementation by specifying something like "IEnumerable.Current" for the string parameter.
  2. 通过为字符串参数指定类似“IEnumerable.Current”的内容来处理显式接口实现。

  3. Define Members.Add so that you don't need to specify the delegate type in the initializer.
  4. 定义Members.Add,这样您就不需要在初始化程序中指定委托类型。

You would need to do a few things to implement this:

你需要做一些事情来实现这个:

  1. Writer a small parser for C# type names. This only requires ".", "[]", "<>",ID, and the primitive type names, so you could probably do that in a few hours
  2. 编写一个C#类型名称的小解析器。这只需要“。”,“[]”,“<>”,ID和原始类型名称,所以你可以在几个小时内完成

  3. Implement a cache so that you only generate a single class for each unique interface
  4. 实现缓存,以便您只为每个唯一的接口生成一个类

  5. Implement the Reflection.Emit code gen. This would probably take about 2 days at the most.
  6. 实现Reflection.Emit代码gen。这最多可能需要2天左右。

#2


It requires c# 4, but the opensource framework impromptu interface can fake this out of the box using DLR proxies internally. The performance is good although not as good as if the change you proposed existed.

它需要c#4,但开源框架即兴接口可以在内部使用DLR代理来伪装这个开箱即用。虽然不如你提出的改变存在,但表现还是不错的。

using ImpromptuInterface.Dynamic;

...

var f = ImpromptuGet.Create<IFoo>(new{ 
                Foo = "foo",
                Print = ReturnVoid.Arguments(() => Console.WriteLine(Foo))
            });

#3


An anonymous type can't be made to do anything except to have read-only properties.

除了具有只读属性之外,不能使匿名类型执行任何操作。

Quoting the C# Programming Guide (Anonymous Types):

引用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."

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

#4


As long as we're putting out an interface wish list, I'd really like to be able to tell the compiler that a class implements an interface outside the class definition- even in a separate assembly.

只要我们提出一个接口愿望清单,我真的希望能够告诉编译器一个类在类定义之外实现一个接口 - 即使是在一个单独的程序集中。

For example, let's say I'm working on a program to extract files from different archive formats. I want to be able to pull in existing implementations from different libraries — say, SharpZipLib and a commercial PGP implementation — and consume both libraries using the same code without creating new classes. Then I could use types from either source in generic constraints, for example.

例如,假设我正在开发一个从不同存档格式中提取文件的程序。我希望能够从不同的库中提取现有的实现 - 比如,SharpZipLib和商业PGP实现 - 并使用相同的代码使用两个库而无需创建新类。然后我可以在通用约束中使用来自任一源的类型。

Another use would be telling the compiler that System.Xml.Serialization.XmlSerializer implements the System.Runtime.Serialization.IFormatter interface (it already does, but the compiler doesn't know it).

另一个用途是告诉编译器System.Xml.Serialization.XmlSerializer实现System.Runtime.Serialization.IFormatter接口(它已经存在,但编译器不知道它)。

This could be used to implement your request as well, just not automatically. You'd still have to explicitly tell the compiler about it. Not sure how the syntax would look, because you'd still have to manually map methods and properties somewhere, which means a lot of verbiage. Maybe something similar to extension methods.

这也可用于实现您的请求,而不是自动执行。您仍然必须明确告诉编译器。不确定语法的外观,因为你仍然需要在某处手动映射方法和属性,这意味着大量的措辞。也许类似于扩展方法。

#5


You could have something like anonymous classes in Java:

您可以使用Java中的匿名类:

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

#6


Wouldn't this be cool. Inline anonymous class:

这不是很酷。内联匿名类:

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

#7


I'm going to dump this here. I wrote it a while ago but IIRC it works OK.

我要把它丢弃在这里。我刚刚写了它,但是IIRC它运作正常。

First a helper function to take a MethodInfo and return a Type of a matching Func or Action. You need a branch for each number of parameters, unfortunately, and I apparently stopped at three.

首先是一个辅助函数,用于获取MethodInfo并返回匹配的Func或Action的Type。不幸的是,你需要为每个参数提供一个分支,而我显然已经停在了三个。

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

And now the method that takes an interface as a generic parameter and returns a Type that implements the interface and has a constructor (needs to be called via Activator.CreateInstance) taking a Func or Action for each method/ getter/setter. You need to know the right order to put them in the constructor, though. Alternatively (commented-out code) it can generate a DLL which you can then reference and use the type directly.

现在这个方法将接口作为泛型参数并返回一个实现接口的Type,并且有一个构造函数(需要通过Activator.CreateInstance调用),为每个方法/ getter / setter取一个Func或Action。但是,您需要知道将它们放在构造函数中的正确顺序。或者(注释掉的代码)它可以生成一个DLL,然后您可以直接引用并使用该类型。

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

You can use this as e.g.

您可以将其用作例如

public interface ITest
{
    void M1();
    string M2(int m2, string n2);
    string prop { get; set; }

    event test BoopBooped;
}

Type it = GenerateInterfaceImplementation<ITest>();
ITest instance = (ITest)Activator.CreateInstance(it,
    new Action(() => {Console.WriteLine("M1 called"); return;}),
    new Func<int, string, string>((i, s) => "M2 gives " + s + i.ToString()),
    new Func<String>(() => "prop value"),
    new Action<string>(s => {Console.WriteLine("prop set to " + s);}),
    new Action<test>(eh => {Console.WriteLine(eh("handler added"));}),
    new Action<test>(eh => {Console.WriteLine(eh("handler removed"));}));

// or with the generated DLL
ITest instance = new DynamicITestWrapper(
    // parameters as before but you can see the signature
    );

#8


Interesting idea, I'd be a little concerned that even if it could be done it might get confusing. E.g. when defining a property with non-trivial setters and getters, or how to disambiguate Foo if the the declaring type also contained a property called Foo.

有趣的想法,我有点担心,即使它可以做到它可能会让人感到困惑。例如。在使用非平凡的setter和getter定义属性时,或者如果声明类型还包含一个名为Foo的属性,如何消除Foo的歧义。

I wonder if this would be easier in a more dynamic language, or even with the dynamic type and DLR in C# 4.0?

我想知道在更动态的语言中,或者使用C#4.0中的动态类型和DLR,这会更容易吗?

Perhaps today in C# some of the intent could be achieved with lambdas:

也许今天在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;}
}

#9


This wouldn't be possible currently.

目前这是不可能的。

What would be the difference between this and simply making IFoo a concrete class instead? Seems like that might be the better option.

这与简单地将IFoo变成具体的课程有什么区别呢?似乎这可能是更好的选择。

What it would take? A new compiler and tons of checks to ensure they didn't break the other features. Personally, I think it'd just be easier to require developers to just create a concrete version of their class.

需要什么?一个新的编译器和大量的检查,以确保它们没有打破其他功能。就个人而言,我认为要求开发人员创建他们类的具体版本会更容易。

#10


I have used in Java the Amonimous Class through the "new IFoo(){...}" sintax and it's practical and easy when you have to quick implement a simple interface.

我通过“new IFoo(){...}”sintax在Java中使用了Amonimous类,当你​​必须快速实现一个简单的接口时,它很实用。

As a sample it would be nice to implement IDisposable this way on a legacy object used just one time instead of deriving a new class to implement it.

作为一个示例,以这种方式在仅使用一次的遗留对象上实现IDisposable而不是派生新类来实现它将是很好的。

#1


There would be a few issues with overloaded members, indexers, and explicit interface implementations.

重载成员,索引器和显式接口实现会有一些问题。

However, you could probably define the syntax in a way that allows you to resolve those problems.

但是,您可以以允许您解决这些问题的方式定义语法。

Interestingly, you can get pretty close to what you want with C# 3.0 by writing a library. Basically, you could do this:

有趣的是,通过编写库,您可以非常接近C#3.0所需的内容。基本上,你可以这样做:

Create<IFoo>
(
    new
    {
        Foo = "foo",
        Print = (Action)(() => Console.WriteLine(Foo))
    }
);

Which is pretty close to what you want. The primary differences are a call to "Create" instead of the "new" keyword and the fact that you need to specify a delegate type.

这与你想要的非常接近。主要区别是调用“Create”而不是“new”关键字,以及需要指定委托类型的事实。

The declaration of "Create" would look like this:

“创建”的声明如下所示:

T Create<T> (object o)
{
//...
}

It would then use Reflection.Emit to generate an interface implementation dynamically at runtime.

然后,它将使用Reflection.Emit在运行时动态生成接口实现。

This syntax, however, does have problems with explicit interface implementations and overloaded members, that you couldn't resolve without changing the compiler.

但是,这种语法确实存在显式接口实现和重载成员的问题,如果不更改编译器就无法解决。

An alternative would be to use a collection initializer rather than an anonymous type. That would look like this:

另一种方法是使用集合初始化程序而不是匿名类型。这看起来像这样:

Create
{
    new Members<IFoo>
    {
        {"Print", ((IFoo @this)=>Console.WriteLine(Foo))},
        {"Foo", "foo"}
    }
}

That would enable you to:

这将使您能够:

  1. Handle explicit interface implementation by specifying something like "IEnumerable.Current" for the string parameter.
  2. 通过为字符串参数指定类似“IEnumerable.Current”的内容来处理显式接口实现。

  3. Define Members.Add so that you don't need to specify the delegate type in the initializer.
  4. 定义Members.Add,这样您就不需要在初始化程序中指定委托类型。

You would need to do a few things to implement this:

你需要做一些事情来实现这个:

  1. Writer a small parser for C# type names. This only requires ".", "[]", "<>",ID, and the primitive type names, so you could probably do that in a few hours
  2. 编写一个C#类型名称的小解析器。这只需要“。”,“[]”,“<>”,ID和原始类型名称,所以你可以在几个小时内完成

  3. Implement a cache so that you only generate a single class for each unique interface
  4. 实现缓存,以便您只为每个唯一的接口生成一个类

  5. Implement the Reflection.Emit code gen. This would probably take about 2 days at the most.
  6. 实现Reflection.Emit代码gen。这最多可能需要2天左右。

#2


It requires c# 4, but the opensource framework impromptu interface can fake this out of the box using DLR proxies internally. The performance is good although not as good as if the change you proposed existed.

它需要c#4,但开源框架即兴接口可以在内部使用DLR代理来伪装这个开箱即用。虽然不如你提出的改变存在,但表现还是不错的。

using ImpromptuInterface.Dynamic;

...

var f = ImpromptuGet.Create<IFoo>(new{ 
                Foo = "foo",
                Print = ReturnVoid.Arguments(() => Console.WriteLine(Foo))
            });

#3


An anonymous type can't be made to do anything except to have read-only properties.

除了具有只读属性之外,不能使匿名类型执行任何操作。

Quoting the C# Programming Guide (Anonymous Types):

引用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."

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

#4


As long as we're putting out an interface wish list, I'd really like to be able to tell the compiler that a class implements an interface outside the class definition- even in a separate assembly.

只要我们提出一个接口愿望清单,我真的希望能够告诉编译器一个类在类定义之外实现一个接口 - 即使是在一个单独的程序集中。

For example, let's say I'm working on a program to extract files from different archive formats. I want to be able to pull in existing implementations from different libraries — say, SharpZipLib and a commercial PGP implementation — and consume both libraries using the same code without creating new classes. Then I could use types from either source in generic constraints, for example.

例如,假设我正在开发一个从不同存档格式中提取文件的程序。我希望能够从不同的库中提取现有的实现 - 比如,SharpZipLib和商业PGP实现 - 并使用相同的代码使用两个库而无需创建新类。然后我可以在通用约束中使用来自任一源的类型。

Another use would be telling the compiler that System.Xml.Serialization.XmlSerializer implements the System.Runtime.Serialization.IFormatter interface (it already does, but the compiler doesn't know it).

另一个用途是告诉编译器System.Xml.Serialization.XmlSerializer实现System.Runtime.Serialization.IFormatter接口(它已经存在,但编译器不知道它)。

This could be used to implement your request as well, just not automatically. You'd still have to explicitly tell the compiler about it. Not sure how the syntax would look, because you'd still have to manually map methods and properties somewhere, which means a lot of verbiage. Maybe something similar to extension methods.

这也可用于实现您的请求,而不是自动执行。您仍然必须明确告诉编译器。不确定语法的外观,因为你仍然需要在某处手动映射方法和属性,这意味着大量的措辞。也许类似于扩展方法。

#5


You could have something like anonymous classes in Java:

您可以使用Java中的匿名类:

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

#6


Wouldn't this be cool. Inline anonymous class:

这不是很酷。内联匿名类:

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

#7


I'm going to dump this here. I wrote it a while ago but IIRC it works OK.

我要把它丢弃在这里。我刚刚写了它,但是IIRC它运作正常。

First a helper function to take a MethodInfo and return a Type of a matching Func or Action. You need a branch for each number of parameters, unfortunately, and I apparently stopped at three.

首先是一个辅助函数,用于获取MethodInfo并返回匹配的Func或Action的Type。不幸的是,你需要为每个参数提供一个分支,而我显然已经停在了三个。

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

And now the method that takes an interface as a generic parameter and returns a Type that implements the interface and has a constructor (needs to be called via Activator.CreateInstance) taking a Func or Action for each method/ getter/setter. You need to know the right order to put them in the constructor, though. Alternatively (commented-out code) it can generate a DLL which you can then reference and use the type directly.

现在这个方法将接口作为泛型参数并返回一个实现接口的Type,并且有一个构造函数(需要通过Activator.CreateInstance调用),为每个方法/ getter / setter取一个Func或Action。但是,您需要知道将它们放在构造函数中的正确顺序。或者(注释掉的代码)它可以生成一个DLL,然后您可以直接引用并使用该类型。

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

You can use this as e.g.

您可以将其用作例如

public interface ITest
{
    void M1();
    string M2(int m2, string n2);
    string prop { get; set; }

    event test BoopBooped;
}

Type it = GenerateInterfaceImplementation<ITest>();
ITest instance = (ITest)Activator.CreateInstance(it,
    new Action(() => {Console.WriteLine("M1 called"); return;}),
    new Func<int, string, string>((i, s) => "M2 gives " + s + i.ToString()),
    new Func<String>(() => "prop value"),
    new Action<string>(s => {Console.WriteLine("prop set to " + s);}),
    new Action<test>(eh => {Console.WriteLine(eh("handler added"));}),
    new Action<test>(eh => {Console.WriteLine(eh("handler removed"));}));

// or with the generated DLL
ITest instance = new DynamicITestWrapper(
    // parameters as before but you can see the signature
    );

#8


Interesting idea, I'd be a little concerned that even if it could be done it might get confusing. E.g. when defining a property with non-trivial setters and getters, or how to disambiguate Foo if the the declaring type also contained a property called Foo.

有趣的想法,我有点担心,即使它可以做到它可能会让人感到困惑。例如。在使用非平凡的setter和getter定义属性时,或者如果声明类型还包含一个名为Foo的属性,如何消除Foo的歧义。

I wonder if this would be easier in a more dynamic language, or even with the dynamic type and DLR in C# 4.0?

我想知道在更动态的语言中,或者使用C#4.0中的动态类型和DLR,这会更容易吗?

Perhaps today in C# some of the intent could be achieved with lambdas:

也许今天在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;}
}

#9


This wouldn't be possible currently.

目前这是不可能的。

What would be the difference between this and simply making IFoo a concrete class instead? Seems like that might be the better option.

这与简单地将IFoo变成具体的课程有什么区别呢?似乎这可能是更好的选择。

What it would take? A new compiler and tons of checks to ensure they didn't break the other features. Personally, I think it'd just be easier to require developers to just create a concrete version of their class.

需要什么?一个新的编译器和大量的检查,以确保它们没有打破其他功能。就个人而言,我认为要求开发人员创建他们类的具体版本会更容易。

#10


I have used in Java the Amonimous Class through the "new IFoo(){...}" sintax and it's practical and easy when you have to quick implement a simple interface.

我通过“new IFoo(){...}”sintax在Java中使用了Amonimous类,当你​​必须快速实现一个简单的接口时,它很实用。

As a sample it would be nice to implement IDisposable this way on a legacy object used just one time instead of deriving a new class to implement it.

作为一个示例,以这种方式在仅使用一次的遗留对象上实现IDisposable而不是派生新类来实现它将是很好的。