假设我正在使用第三方库中的多个类,所有这些类都包含int ID {get;}
属性。例如:
class A {
...
int ID {get;}
...
}
class B {
...
int ID {get;}
...
}
ID
是我需要在我正在编写的方法中使用的唯一属性,它需要引用A或B.我已经声明了一个接口:
interface IHasID
{
int ID {get;}
}
但是在调用我的方法时,我无法将A
或B
视为IHasID
。
例如,在Swift中,我可以声明一个符合“协议”(Swift的接口版本)的扩展。我没有看到用C#做到这一点的方法。引用共享属性的不相关类的最接近方式是什么?
不,目前没办法做到这一点。你可以:
ID
,而无需在编译时进行类型检查此功能可能会在某个时间添加到C#中。有关细节,请参阅Mads Torgersen的issue about shapes - 但我怀疑它还有很长的路要走。
您可以使用通用方法以及ID的选择器,而不是包装类。这里的用法很简单,但在某些情况下这种模式很有用。
public void MyMethod<T>(T value, Func<T, int> selectId)
{
var id = selectId(value);
}
和
MyMethod<A>(a, value => value.ID);
MyMethod<B>(b, value => value.ID);
可以缩短为
MyMethod(a, value => value.ID);
MyMethod(b, value => value.ID);
请注意,即使它们看起来相同,但两个lambda不完全相同。
Func<A, int>
vs Func<B, int>
创建一个具有以下所有内容的通用包装类:
IHasID
接口的实现,T
和Func<T,int>
来获取ID,以及这是一个例子:
class HasId<T> : IHasId {
private readonly Func<T,int> idGetter;
public Value {get;}
public Id => idGetter(Value);
public HasId(T val, Func<T,int> idGetter) {
Value = value;
this.idGetter = idGetter;
}
}
如果您想将编写适配器作为解决方案,那么这是将类型连接到层次结构中的典型实现。
public abstract class HasIDAdapter : IHasID {
public abstract int ID {get;}
private class AAdapter : HasIDAdapter {
A _a;
public AAdapter(A a) {
_a = a;
}
public override int ID { get => _a.ID; }
}
private class BAdapter : HasIDAdapter {
B _b;
public BAdapter(B b) {
_b = b;
}
public override int ID {get => _b.ID;}
}
public static implicit operator HasIDAdapter(A a) => new AAdapter(a);
public static implicit operator HasIDAdapter(B b) => new BAdapter(b);
}
这种推动问题,你仍然需要在使用界面之前从A
或B
转换为HasIDAdapter
,但这可以通过将HasIDAdapter
作为方法参数,或通过将A
或B
实例分配给HasIDAdapter
类型的变量在传递给IHasID
方法之前。
另一种(不那么优化)的解决方案是使用反射:
static int IdOf<T>(T t)
{
return (int)typeof(T).GetProperty("ID").GetValue(t);
}
我真的不喜欢这个解决方案,但我还是把它包括在内,因为它是一个选择......