C# 中的存在类型?
Existential types in C#?
我目前在 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);
}
}
接下来我希望能够遍历实现此接口的对象列表,但不关心它具有什么类型参数。像这样:
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);
}
}
这不会编译,因为 MyIntClass
使用的 T
和 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);
}
}
无法直接在 C# 中使用。
您可以放弃类型安全并拥有非通用基础接口并将其用于 "generic" 代码:
public interface MyInterface
{
object GetSomething();
void DoSomething(object something);
}
public interface MyInterface<T> : MyInterface
{
T GetSomething();
void DoSomething(T something);
}
或使用dynamic
(同样没有编译时类型安全):
foreach (dynamic thingDoer in listOfThings)
{
dynamic something = thingDoer.GetSomething();
thingDoer.DoSomething(something);
thingDoer.DoSomething(something);
}
或者生成多个版本的处理程序并根据类型创建(可能使用缓存)(How do I use reflection to call a generic method?)(注意:你不能真正表达 "list of arbitrary objects" 比 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);
}
反射的替代方法是使用表达式树来构建类似的代码。
由于 DoALotOfThingsTwice
不依赖于 T
,您可以将其包装在 Action
中并将其存储在列表中,例如
public static Action DoSomethingTwice<T>(this MyInterface<T> i)
{
return () =>
{
T something = i.GetSomething();
i.DoSomething(something);
i.DoSomething(something);
};
}
然后
var listOfThings = new List<Action>() {
new MyIntClass().DoSomethingTwice(),
new MyStringClass().DoSomethingTwice()
};
由于我不知道您实际域中的潜在问题是什么,因此我无法提供万无一失的解决方案。您应该了解一下 what's covariance and contravariance in generic parameters both on interfaces and delegates 并尝试重构您的代码,这应该是值得的。
目前,我认为这应该是一个可能的解决方案:
public static void DoALotOfThingsTwice()
{
var listOfThings = new List<object>
{
new MyIntClass(), new MyStringClass()
};
MyInterface<int> a;
MyInterface<string> b;
// During each iteration, check if the thing is a concrete
// implementation of your interface MyInterface<T>...
foreach (object thingDoer in listOfThings)
{
// ...and call MyInterface<T>.DoSomething method depending on
// the success of the cast to MyInterface<int> or
// MyInterface<string>
if ((a = thingDoer as MyInterface<int>) != null)
a.DoSomething(38);
else if((b = thingDoer as MyInterface<string>) != null)
b.DoSomething("hello world");
}
}
这实际上可以以完全类型安全的方式实现,无需任何类型转换,也无需对接口函数的作用做任何假设。
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());
}
}
}
我目前在 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);
}
}
接下来我希望能够遍历实现此接口的对象列表,但不关心它具有什么类型参数。像这样:
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);
}
}
这不会编译,因为 MyIntClass
使用的 T
和 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);
}
}
无法直接在 C# 中使用。
您可以放弃类型安全并拥有非通用基础接口并将其用于 "generic" 代码:
public interface MyInterface
{
object GetSomething();
void DoSomething(object something);
}
public interface MyInterface<T> : MyInterface
{
T GetSomething();
void DoSomething(T something);
}
或使用dynamic
(同样没有编译时类型安全):
foreach (dynamic thingDoer in listOfThings)
{
dynamic something = thingDoer.GetSomething();
thingDoer.DoSomething(something);
thingDoer.DoSomething(something);
}
或者生成多个版本的处理程序并根据类型创建(可能使用缓存)(How do I use reflection to call a generic method?)(注意:你不能真正表达 "list of arbitrary objects" 比 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);
}
反射的替代方法是使用表达式树来构建类似的代码。
由于 DoALotOfThingsTwice
不依赖于 T
,您可以将其包装在 Action
中并将其存储在列表中,例如
public static Action DoSomethingTwice<T>(this MyInterface<T> i)
{
return () =>
{
T something = i.GetSomething();
i.DoSomething(something);
i.DoSomething(something);
};
}
然后
var listOfThings = new List<Action>() {
new MyIntClass().DoSomethingTwice(),
new MyStringClass().DoSomethingTwice()
};
由于我不知道您实际域中的潜在问题是什么,因此我无法提供万无一失的解决方案。您应该了解一下 what's covariance and contravariance in generic parameters both on interfaces and delegates 并尝试重构您的代码,这应该是值得的。
目前,我认为这应该是一个可能的解决方案:
public static void DoALotOfThingsTwice()
{
var listOfThings = new List<object>
{
new MyIntClass(), new MyStringClass()
};
MyInterface<int> a;
MyInterface<string> b;
// During each iteration, check if the thing is a concrete
// implementation of your interface MyInterface<T>...
foreach (object thingDoer in listOfThings)
{
// ...and call MyInterface<T>.DoSomething method depending on
// the success of the cast to MyInterface<int> or
// MyInterface<string>
if ((a = thingDoer as MyInterface<int>) != null)
a.DoSomething(38);
else if((b = thingDoer as MyInterface<string>) != null)
b.DoSomething("hello world");
}
}
这实际上可以以完全类型安全的方式实现,无需任何类型转换,也无需对接口函数的作用做任何假设。
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());
}
}
}