我正在使用来自第三方库的2个类,我们称之为Obj1和Obj2。它们具有几乎相同的集合属性和方法,节省了一些。两者都是独立的对象,不会从任何常见的东西继承。
问题在于,在我的代码中,我必须为这两个对象提供2个路径,即使我对它们所做的工作是相同的。例如:
void Foo1(Obj1 obj) {
obj.Cost = calcCostObj1(obj);
obj.Rate = generateRateObj1(obj);
... // 50 more lines of this
}
void Foo2(Obj2 obj) {
obj.Cost = calcCostObj2(obj);
obj.Rate = generateRateObj2(obj);
...
}
当然,我可以重载方法,但它仍然是两种方法,几乎每个操作......
不幸的是,我无法修改第三方库。在C#中是否有一种方法(不太可能,我知道),我可以在某种程度上将这两个类统一到一个接口中,这样我就不必有两种方法来处理所有事情。
或至少使情况具体化。
P.S。我的梦想场景(目前C#无法实现):
void Foo<T>(T obj) where T: Obj1, Obj2 {
obj.Cost = calcCostObj<T>(obj);
obj.Rate = calcRateObj<T>(obj);
}
// calling code
Obj1 obj = new Obj1();
Foo<Obj1>(obj);
答案 0 :(得分:5)
您可以创建一个接口,以及通过转发对Obj1和Obj2的调用来实现它的两个代理类。
interface IObj {
int calcCostObj1();
...
}
class Obj1Wrapper: IObj {
Obj1 obj;
int calcCostObj1() {
return obj1.calcCostObj1();
}
}
如果您使用Resharper,则可以使用“委派成员”重构生成这些代理类
答案 1 :(得分:1)
如果您可以访问dynamic
,则可以完全实现您的梦想场景:
var obj = new Obj1();
Foo<Obj1>(obj);
这是running code sample。它具有通用的Foo<T>(dynamic obj)
方法,可确保obj
在运行时的类型为T
。从某种意义上说,你将通用的where
放在方法中。你只是没有进行编译时类型检查。
public class Program
{
public static void Foo<T>(dynamic obj)
{
if (!(obj is T))
{
var message =
string.Format("Expecting type of <{0}>.", typeof(T).Name);
throw new System.ArgumentException(message);
}
obj.Cost = "11.99";
obj.Rate = "0.50";
// obj.Nope = ""; // RuntimeBinderException
}
public static void Main()
{
var obj = new Obj1();
Foo<Obj1>(obj);
System.Console.WriteLine("${0} at ${1}/hr.", obj.Cost, obj.Rate);
// Foo<Obj1>(""); // ArgumentException
}
}
public class Obj1
{
public string Cost, Rate;
}
public class Obj2
{
public string Cost, Rate;
}
T
以上并没有实际约束T
,所以如果你还需要约束T
,那么你可以使用它:
public static void Bar<T>(dynamic obj)
{
bool IsTypeRight =
(typeof(Obj1) == typeof(T) || typeof(Obj2) == typeof(T));
bool IsObjRight = obj is T;
if (!IsTypeRight || !IsObjRight)
{
var message =
string.Format("Expecting type of <{0}>.", typeof(T).Name);
throw new System.ArgumentException(message);
}
// ...
}
object
它需要dynamic
,因为object
不包含Cost
的定义,您需要将对象转换为Obj1
或Obj2
。这导致代码重复。如果我们可以将其转换为T
,但C#不允许这样做,那就太好了。
public static void Foo<T>(object obj)
{
if (!(obj is T))
{
var message =
string.Format("Expecting type of <{0}>.", typeof(T).Name);
throw new System.ArgumentException(message);
}
// Compiler: 'object' does not contain a definition for 'Cost'
// obj.Cost = "11.99";
if(obj is Obj1)
{
(obj as Obj1).Cost = "11.99";
}
else if(obj is Obj2)
{
(obj as Obj2).Cost = "11.99";
}
}
答案 2 :(得分:1)
作为亚历克斯的答案,你应该将你的对象包装在一个薄的包装中,暴露出普通的成员。但是,通过让包装器继承自公共抽象类而不是接口,您可以更好地使代码透明化。这样,您就可以拥有从Obj1
和Obj2
到它的隐式转换器。
abstract class BaseObj
{
public abstract double Cost { get; set; };
public abstract double Rate{ get; set; }
//...
}
class Obj1Wrapper : BaseObj
{
private Obj1 _obj;
public Obj1Wrapper(Obj1 obj)
{
this._obj = obj;
}
public override double Cost
{
get
{
return this._obj.Cost;
}
set
{
this._obj.Cost = value;
}
};
public override double Rate
{
get
{
return this._obj.Rate;
}
set
{
this._obj.Rate= value;
}
};
//...
public static implicit operator BaseObj(Obj1 obj)
{
return new Obj1Wrapper(obj);
}
}
class Obj2Wrapper : BaseObj
{
private Obj2 _obj;
public Obj2Wrapper(Obj2 obj)
{
this._obj = obj;
}
public override double Cost
{
get
{
return this._obj.Cost;
}
set
{
this._obj.Cost = value;
}
};
public override double Rate
{
get
{
return this._obj.Rate;
}
set
{
this._obj.Rate= value;
}
};
//...
public static implicit operator BaseObj(Obj2 obj)
{
return new Obj2Wrapper(obj);
}
}
现在,您可以在任何期望Obj1
的方法中无差别地使用Obj2
或BaseObj
作为参数。