我已经提供了两个库,每个库都有自己的命名空间,它们对于api的请求部分具有相同的类结构,但响应不同。
我想编写一个可以处理与其命名空间类型无关的请求的函数(我已尝试使用泛型但未能创建解决方案)。
这是我想要做的一个例子:
public void Function1()
{
Namespace1.MyType type1 = new Namespace1.MyType1();
type1.InnerType = new Namespace1.MyType2();
type1.PerformMethod1();
}
public void Function2()
{
Namespace2.MyType type1 = new Namespace2.MyType1();
type1.InnerType = new Namespace2.MyType2();
type1.PerformMethod1();
}
我试图通过以下方式在方法级别使用泛型:
public void Function1<TType1, TType2>()
where TType1: NamesSpace1.Type1, NamesSpace2.Type1, new()
where TType2: Namespace1.Type2. Namespace2.Type2(), new()
{
TType1 type1 = new TType1();
type1.InnerType = new TType2();
type1.PerformMethod1();
}
但是,.net不允许对泛型进行此级别的限制。 关于如何创建通用函数的任何建议都会很棒。
由于
答案 0 :(得分:2)
简短的回答是:你不能。
C#(或.NET)不支持“Structural Subtyping”,使这两个类完全分开,尽管它们具有相同的成员。
但是,您可以使用dynamic
关键字,但是您将失去所有编译时类型的安全性。
publiv void Function(dynamic type1)
{
type1.PerformMethod1();
}
你仍然需要创建type1
和type2
这两个不同的实例,所以你在这里获得的收益并不多。
具有基本相同缺点的另一种可能的解决方案是使用反射来创建类型:
public void Function(string assemblyName, string namespace)
{
var type1Name = namespace + ".Type1, " + assemblyName;
var type2Name = namespace + ".Type2, " + assemblyName;
dynamic type1 = Activator.CreateInstance(Type.GetType(type1Name));
dynamic type2 = Activator.CreateInstance(Type.GetType(type1Name));
type1.InnerType = type2;
type1.PerformMethod1();
}
第三种解决方案是仅使用带有new()
约束的泛型,以便能够更轻松地创建实例并使用运行时类型检查来确保仅使用了允许的类型。同样,这与其他两个解决方案具有相同的缺点:无编译时类型安全性:
public void Function1<TType1, TType2>()
where TType1 : new()
where TType2 : new()
{
if(typeof(TType1) != typeof(Namespace1.MyType1) ||
typeof(TType1) != typeof(Namespace2.MyType1) ||
typeof(TType2) != typeof(Namespace1.MyType2) ||
typeof(TType2) != typeof(Namespace2.MyType2))
throw new ArgumentException(...);
// .NET 4:
dynamic type1 = new TType1();
dynamic type2 = new TType2();
// Pre .NET 4:
// var type1 = new TType1();
// var type2 = new TType2();
// .NET 4:
type1.InnerType = type2;
// Pre .NET 4:
// type1.GetType().GetProperty("InnerType").SetValue(type1, type2);
// .NET 4:
type1.PerformMethod1();
// Pre .NET 4:
// type1.GetType().GetMethod("PerformMethod1").Invoke(type1, new object[0]);
}
答案 1 :(得分:1)
我认为最好的方法是创建一个通用接口,并以某种方式适应库类。问题是如何做到这一点。
如果有问题的类型没有被密封并且你自己创建它的实例,那么你可以创建一个实现这个接口的派生类,它将由继承的方法自动实现:
interface IMyType
{
object InnerType { get; set; }
void PerformMethod1();
}
class Namespace1MyTypeWithInterface : Namespace1.MyType, IMyType
{}
这假设两种类型实际上是相同的。例如,如果InnerType
的类型各不相同,则情况会变得更加复杂。解决这个问题的一种方法是在属性类型中使接口通用。
如果之前的解决方案不适合您,那么您可以为每个命名空间创建适配器类。两个适配器将再次共享相同的接口,并且它们会将每个调用委托给实际类型的实例。
class Namespace1MyTypeAdapter : IMyType
{
private readonly Namespace1.MyType m_adapted;
public Namespace1MyTypeAdapter(Namespace1.MyType adapted)
{
m_adapted = adapted;
}
public object InnerType
{
get { return m_adapted.InnerType; }
set { m_adapted.InnerType = value; }
}
public void PerformMethod1()
{
m_adapted.PerformMethod1();
}
}
它有很多重复的代码,但你写了一次然后你可以用同样的方式使用这两个类。
另一个选择是使用dynamic
或反射。