gon*_*zaw 12 c# polymorphism existential-type
我目前正面临C#中的问题,我认为可以使用存在类型来解决.但是,我真的不知道它们是可以用C#创建还是模拟(使用其他构造).
基本上我想要一些像这样的代码:
public interface MyInterface<T>
{
T GetSomething();
void DoSomething(T something);
}
public class MyIntClass : MyInterface<int>
{
int GetSomething()
{
return 42;
}
void DoSomething(int something)
{
Console.Write(something);
}
}
public class MyStringClass : MyInterface<string>
{
string GetSomething()
{
return "Something";
}
void DoSomething(string something)
{
SomeStaticClass.DoSomethingWithString(something);
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,我希望能够遍历实现此接口的对象列表,但不关心它具有什么类型参数.像这样的东西:
public static void DoALotOfThingsTwice(){
var listOfThings = new List<MyInterface<T>>(){
new MyIntClass(),
new MyStringClass();
};
foreach (MyInterface<T> thingDoer in listOfThings){
T something = thingDoer.GetSomething();
thingDoer.DoSomething(something);
thingDoer.DoSomething(something);
}
}
Run Code Online (Sandbox Code Playgroud)
这不会编译,因为T使用的MyIntClass和使用的MyStringClass是不同的.
我认为像这样的东西可以解决问题,但我不知道在C#中是否有一种有效的方法:
public static void DoALotOfThingsTwice(){
var listOfThings = new List<?T.MyInterface<T>>(){
new MyIntClass(),
new MyStringClass();
};
foreach (?T.MyInterface<T> thingDoer in listOfThings){
T something = thingDoer.GetSomething();
thingDoer.DoSomething(something);
thingDoer.DoSomething(something);
}
}
Run Code Online (Sandbox Code Playgroud)
不可能直接在C#中.
您可以删除类型安全性并具有非通用基本接口,并将其用于"通用"代码:
public interface MyInterface
{
object GetSomething();
void DoSomething(object something);
}
public interface MyInterface<T> : MyInterface
{
T GetSomething();
void DoSomething(T something);
}
Run Code Online (Sandbox Code Playgroud)
或者使用dynamic(再次没有编译时类型安全):
foreach (dynamic thingDoer in listOfThings)
{
dynamic something = thingDoer.GetSomething();
thingDoer.DoSomething(something);
thingDoer.DoSomething(something);
}
Run Code Online (Sandbox Code Playgroud)
或基于类型生成处理程序的多个版本,并创建(可能与缓存)(如何使用反射来调用一个泛型方法?(注):你能不能真正表达比"任意对象的名单" List<object>或List<NonGenericBaseInterface>或 List<NonGenericBaseClass>):
foreach (object thingDoer in listOfThings)
{
// get Do via reflection and create specific version based on
// thingDoer.GetType(), than invoke
// consider caching "methodForType" in Dictionary by type
MethodInfo method = this.GetType().GetMethod("Do");
MethodInfo methodForType = method.MakeGenericMethod(thingDoer.GetType());
methodForType.Invoke(thingDoer, null);
}
void Do<T>( MyInterface<T> thingDoer)
{
T something = thingDoer.GetSomething();
thingDoer.DoSomething(something);
thingDoer.DoSomething(something);
}
Run Code Online (Sandbox Code Playgroud)
反射的替代方法是使用表达式树来构建类似的代码.
因为DoALotOfThingsTwice不依赖于T你可以将它包装在一个Action并存储在列表中,例如
public static Action DoSomethingTwice<T>(this MyInterface<T> i)
{
return () =>
{
T something = i.GetSomething();
i.DoSomething(something);
i.DoSomething(something);
};
}
Run Code Online (Sandbox Code Playgroud)
然后
var listOfThings = new List<Action>() {
new MyIntClass().DoSomethingTwice(),
new MyStringClass().DoSomethingTwice()
};
Run Code Online (Sandbox Code Playgroud)
这实际上以完全类型安全的方式是可能的,没有任何类型转换,也没有关于接口函数做什么的任何假设。
https://dotnetfiddle.net/buneul
using System;
using System.Collections;
using System.Collections.Generic;
public interface MyInterfaceFunc {
void Call<T>(MyInterface<T> obj);
}
public interface MyInterface {
void Generically(MyInterfaceFunc func); // this is the key!
}
public interface MyInterface<T> : MyInterface
{
T GetSomething();
void DoSomething(T something);
}
public class MyIntClass : MyInterface<int>
{
public int GetSomething()
{
return 42;
}
public void DoSomething(int something)
{
Console.Write(something);
}
public void Generically(MyInterfaceFunc func) {
func.Call(this);
}
}
public class MyStringClass : MyInterface<string>
{
public string GetSomething()
{
return "Something";
}
public void DoSomething(string something)
{
Console.Write(something);
}
public void Generically(MyInterfaceFunc func) {
func.Call(this);
}
}
public class MyFunc : MyInterfaceFunc {
public void Call<T>(MyInterface<T> thingDoer) {
T something = thingDoer.GetSomething();
thingDoer.DoSomething(something);
thingDoer.DoSomething(something);
}
}
public class Program {
public static void Main(){
var listOfThings = new List<MyInterface>(){
new MyIntClass(),
new MyStringClass()
};
foreach (MyInterface thingDoer in listOfThings){
thingDoer.Generically(new MyFunc());
}
}
}
Run Code Online (Sandbox Code Playgroud)