如何将2个物体统一成一个连贯的立面?

时间:2015-03-11 00:02:56

标签: c#

我正在使用来自第三方库的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);

3 个答案:

答案 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的定义,您需要将对象转换为Obj1Obj2。这导致代码重复。如果我们可以将其转换为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)

作为亚历克斯的答案,你应该将你的对象包装在一个薄的包装中,暴露出普通的成员。但是,通过让包装器继承自公共抽象类而不是接口,您可以更好地使代码透明化。这样,您就可以拥有从Obj1Obj2到它的隐式转换器。

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的方法中无差别地使用Obj2BaseObj作为参数。