传递可以 "fit" 接口的 C# 参数,但实际上并不实现它
Passing C# parameters which can "fit" an interface, but do not actually implement it
注意:我知道这在实践中是一个糟糕的想法;我只是好奇 CLR 允许你做什么,目标是创建某种 'modify a class after creating it' 预处理器。
假设我有以下 class,它是在另一个程序集中定义的,所以我无法更改它。
class Person {
public string Greet() => "Hello!";
}
我现在定义一个接口和一个方法,如下所示:
interface IGreetable {
string Greet();
}
// ...
void PrintGreeting(IGreetable g) => Console.WriteLine(g.Greet());
class Person
没有明确实现 IGreetable
,但 可以 在不对其方法进行任何修改的情况下实现。
有了这个,有什么方法可以使用反射、DLR 或其他任何方法,在不修改上述任何代码的情况下,可以将 Person
的实例成功传递给 PrintGreeting
?
我不相信这是可能的。编译器需要查看明确实现接口或 class 的内容,以便编译器可以确认所有内容均已实现。
如果您可以使用重定向来做到这一点,那么您可能无法实现某些东西。这与 .NET 所采用的安全方法背道而驰。
一个选项是在 person 上创建包装器 class 并将此包装器传递给方法,包装器需要显式实现接口。
尝试使用库Impromptu-Interface
[The Impromptu-Interface] framework to allow you to wrap any object (static or dynamic) with a static interface even though it didn't inherit from it. It does this by emitting cached dynamic binding code inside a proxy.
这允许你做这样的事情:
var person = new Person();
var greeter = person.ActLike<IGreetable>();
您可以使用 dynamic
包装器对象自行连接,但在包装内会失去类型安全性 class:
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();
}
在某种不相关的情况下,这是其他语言(例如 Scala 和 Haskell 中通常会做的事情。
所谓的使用"type classes"。类型 类 本质上允许您为类型定义行为,就好像它显式实现了接口一样,而不需要实际要求它这样做。您可以阅读更多相关信息 here。
这可能很快就会变得很容易。类型 classes 可能会作为 shapes 引入 C#,您可以在其中定义 class 的功能并针对 [=22] 编写代码=]shape 然后将您的代码与任何匹配的类型一起使用,而无需该代码的作者声明任何内容,就像您描述的那样。
现在 C# 中最接近的事情可能是 foreach
如何处理具有 GetEnumerator()
的类型返回具有 MoveNext()
和 [=13= 的类型的对象] 即使它们没有实现 IEnumerable
等,只是当这是编译器处理的内置概念时,您可以在此处定义它们。
有趣的是,它还可以让您定义静态成员。
如果您可以控制外部代码,并且愿意包装对象(似乎这里所有的答案都包装),动态绑定和像 Impromptu-Interface 这样的库在我看来很麻烦对于本质上是一个班轮的东西。
class GreetablePerson : Person, IGreetable { }
大功告成。
当编译器构建 GreetablePerson class 时,来自 Person 的方法最终会隐式实现接口,所有的一切 "just works." 唯一令人恼火的是外部代码必须实例化 GreetablePerson
对象,但是在标准的面向对象术语中,GreetablePerson
的实例是 Person
的实例,所以在我看来这就像一个所问问题的有效答案。
如果要求发生变化,您也有预先存在的 Person 实例,那么像 Impromptu-Interface 这样的东西可能会变得更有吸引力,但即便如此,您可能还是想考虑给 GreetablePerson
一个构造函数来自 Person 的副本。从那里选择最佳前进路径需要获得有关人员 class 的要求和实际实施细节的更多详细信息。
注意:我知道这在实践中是一个糟糕的想法;我只是好奇 CLR 允许你做什么,目标是创建某种 'modify a class after creating it' 预处理器。
假设我有以下 class,它是在另一个程序集中定义的,所以我无法更改它。
class Person {
public string Greet() => "Hello!";
}
我现在定义一个接口和一个方法,如下所示:
interface IGreetable {
string Greet();
}
// ...
void PrintGreeting(IGreetable g) => Console.WriteLine(g.Greet());
class Person
没有明确实现 IGreetable
,但 可以 在不对其方法进行任何修改的情况下实现。
有了这个,有什么方法可以使用反射、DLR 或其他任何方法,在不修改上述任何代码的情况下,可以将 Person
的实例成功传递给 PrintGreeting
?
我不相信这是可能的。编译器需要查看明确实现接口或 class 的内容,以便编译器可以确认所有内容均已实现。
如果您可以使用重定向来做到这一点,那么您可能无法实现某些东西。这与 .NET 所采用的安全方法背道而驰。
一个选项是在 person 上创建包装器 class 并将此包装器传递给方法,包装器需要显式实现接口。
尝试使用库Impromptu-Interface
[The Impromptu-Interface] framework to allow you to wrap any object (static or dynamic) with a static interface even though it didn't inherit from it. It does this by emitting cached dynamic binding code inside a proxy.
这允许你做这样的事情:
var person = new Person();
var greeter = person.ActLike<IGreetable>();
您可以使用 dynamic
包装器对象自行连接,但在包装内会失去类型安全性 class:
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();
}
在某种不相关的情况下,这是其他语言(例如 Scala 和 Haskell 中通常会做的事情。
所谓的使用"type classes"。类型 类 本质上允许您为类型定义行为,就好像它显式实现了接口一样,而不需要实际要求它这样做。您可以阅读更多相关信息 here。
这可能很快就会变得很容易。类型 classes 可能会作为 shapes 引入 C#,您可以在其中定义 class 的功能并针对 [=22] 编写代码=]shape 然后将您的代码与任何匹配的类型一起使用,而无需该代码的作者声明任何内容,就像您描述的那样。
现在 C# 中最接近的事情可能是 foreach
如何处理具有 GetEnumerator()
的类型返回具有 MoveNext()
和 [=13= 的类型的对象] 即使它们没有实现 IEnumerable
等,只是当这是编译器处理的内置概念时,您可以在此处定义它们。
有趣的是,它还可以让您定义静态成员。
如果您可以控制外部代码,并且愿意包装对象(似乎这里所有的答案都包装),动态绑定和像 Impromptu-Interface 这样的库在我看来很麻烦对于本质上是一个班轮的东西。
class GreetablePerson : Person, IGreetable { }
大功告成。
当编译器构建 GreetablePerson class 时,来自 Person 的方法最终会隐式实现接口,所有的一切 "just works." 唯一令人恼火的是外部代码必须实例化 GreetablePerson
对象,但是在标准的面向对象术语中,GreetablePerson
的实例是 Person
的实例,所以在我看来这就像一个所问问题的有效答案。
如果要求发生变化,您也有预先存在的 Person 实例,那么像 Impromptu-Interface 这样的东西可能会变得更有吸引力,但即便如此,您可能还是想考虑给 GreetablePerson
一个构造函数来自 Person 的副本。从那里选择最佳前进路径需要获得有关人员 class 的要求和实际实施细节的更多详细信息。