代码之家  ›  专栏  ›  技术社区  ›  Aaron Christiansen

传递C#参数,这些参数可以“适合”接口,但实际上并不实现它

  •  33
  • Aaron Christiansen  · 技术社区  · 6 年前

    注:我知道这在实践中是一个糟糕的想法;我只是好奇CLR允许您做什么,目的是创建某种“创建类后修改类”预处理器。

    假设我有以下类,它是在另一个程序集中定义的,因此我无法更改它。

    class Person {
        public string Greet() => "Hello!";
    }
    

    我现在定义一个接口和一个方法,如下所示:

    interface IGreetable {
        string Greet();
    } 
    
    // ...
    
    void PrintGreeting(IGreetable g) => Console.WriteLine(g.Greet());
    

    班级 Person 不明确实施 IGreetable ,但它 能够 不要对其方法进行任何修改。

    那么,是否有任何方式,使用反射、DLR或其他任何东西,在其中 可以成功传递给 PrintGreeting 不修改上面的任何代码?

    7 回复  |  直到 6 年前
        1
  •  28
  •   user2864740    6 年前

    尝试使用库 Impromptu-Interface

    [即兴接口]框架,允许您使用静态接口包装任何对象(静态或动态),即使它没有继承自静态接口。 它通过在代理内发出缓存的动态绑定代码来实现这一点。

    这允许您执行以下操作:

    var person = new Person();
    var greeter = person.ActLike<IGreetable>();
    
        2
  •  7
  •   John Koerner    6 年前

    你可以使用 dynamic wrapper对象自行连接,但在wrapping类中失去了类型安全性:

    class GreetableWrapper : IGreetable
    {
        private dynamic _wrapped;
        public GreetableWrapper(dynamic wrapped)
        {
            _wrapped = wrapped;
        }
    
        public string Greet()
        {
            return _wrapped.Greet();
        }
    }
    
    static void PrintGreeting(IGreetable g) => Console.WriteLine(g.Greet());
    static void Main(string[] args)
    {
        PrintGreeting(new GreetableWrapper(new Person()));
        Console.ReadLine();
    }
    
        3
  •  6
  •   Jon Hanna    6 年前

    这可能很快就会很容易。类型类可引入C#as shapes 您可以在其中定义类的功能,并根据这些功能编写代码 形状 然后将您的代码用于任何匹配的类型,而无需代码的作者声明任何内容,就像您描述的那样。

    目前C语言中最接近的事情可能是 foreach 使用具有 GetEnumerator() 返回类型为的对象 MoveNext() Current 即使他们不执行 IEnumerable 只有当这是编译器处理的内置概念时,才可以在这里定义它们。

    有趣的是,它还允许您定义静态成员。

        4
  •  4
  •   Jonathan Wood    6 年前

    我认为这是不可能的。编译器需要查看显式实现接口或类的内容,以便编译器可以确认所有内容都已实现。

    如果可以使用重定向来实现,则可能无法实现某些功能。这与所采用的安全方法背道而驰。净额。

        5
  •  4
  •   Mauri    6 年前

    一个选项是在person上创建一个包装器类并将此包装器传递给方法,包装器需要显式实现接口。

        6
  •  4
  •   GrandOpener    6 年前

    如果您可以控制外部代码,并且愿意包装对象(这里的所有答案似乎都是包装的),那么动态绑定和库(如Impromptu Interface)在我看来,对于本质上是一行代码的东西来说似乎有很多麻烦。

    class GreetablePerson : Person, IGreetable { }
    

    你就完了。

    当编译器构建GreetablePerson类时,Person的方法最终会执行接口的隐式实现,并且一切都“正常工作”唯一恼人的是外部的代码必须实例化 GreetablePerson 对象,但在标准的面向对象术语中 GreetablePerson公司 的实例 Person ,所以在我看来,这似乎是对所问问题的有效答案。

    如果需求发生了变化,您也有了Person的预先存在的实例,那么像即兴界面这样的东西可能会变得更诱人,但即使如此,您也可能会考虑提供 GreetablePerson公司 从Person复制的构造函数。从那里选择最佳的前进路径需要获得有关需求的更多细节以及所讨论的Person类的实际实现细节。

        7
  •  1
  •   wheeler Soth    6 年前

    在某种程度上,这是一种不相关的情况,在其他语言中通常都会这样做,例如Scala和Haskell。

    它被称为使用所谓的“类型类”。类型类本质上允许您定义类型的行为,就好像它显式实现了一个接口一样,而实际上不需要它这样做。你可以了解更多 here .