0

我有一个抽象基类P。我有两个继承P称为I和的类O

我的存储库中有一个Get()返回P. I此方法总是返回一个或O取决于一个值的实例。

public P Get(string pci)
{
     var a = GetAByPCI(string pci);

     if(a == 1)
         return new I();
     else if(a == 2)
         return new O();
     else
         // throw exception here.
}

当我从另一个位置调用此方法时,我需要检查并转换类型。

P p = PRepository.Get("200");

if(p is I)
    I i = (I)p;

我想避免每次使用p上述内容时都必须检查和沮丧。我觉得好像我在这里做了一些根本性的错误,但不确定。

更新: 派生类型I&O具有需要使用但仅与该特定类型相关的其他属性。P有许多在继承类型中实现的抽象属性。向下转换是我可以根据需要获得具体类型的唯一方法吗?

也许这个逻辑是完全错误的。

4

6 回答 6

2

拥有基类的全部意义在于避免不得不处理子类本身。只需向基类添加一个抽象方法并在子类中实现它。这样,调用代码将简单地调用该方法,而无需进行任何强制转换。这是基本的多态性。

于 2013-02-06T21:02:43.340 回答
1

你的意图是什么?看来您没有正确使用继承。如果从方法返回 P 对象,则需要向下转换或返回派生类型。

于 2013-02-06T21:05:11.640 回答
1

你是正确的怀疑向下铸造。

我会看看模板方法设计模式。如果你有一个基类,它应该用来隐藏子类的细节。

因此,也许您的基类中有一个名为 Execute() 的方法。在基类内部,execute 方法调用一些受保护的方法,比如 Method1() 和 Method2()。方法 1 包含共享代码,方法 2 是抽象的,子类必须执行它。然后,当您取回您的实例时,您只需调用 Execute。正确的 Method2() 将运行而无需向下投射。

public abstract class MyBaseClass
{
    public void DoSomething()
    {
        //Here's a bunch of stuff I have to do everytime
        DoSomethingTypeSpecific();
        //I could do more stuff if I needed to
    }

    protected abstract void DoSomethingTypeSpecific();
}

class MyBaseClassOne : MyBaseClass
{
    protected override void DoSomethingTypeSpecific()
    {
        Console.WriteLine(1);
    }
}

class MyBaseClassTwo : MyBaseClass
{
    protected override void DoSomethingTypeSpecific()
    {
        Console.WriteLine(2);
    }
}
于 2013-02-06T21:12:33.883 回答
0

我也会考虑使用Decorator 模式组合而不是使用继承

尽管如此,如果您想访问不属于基类的属性,您仍然需要强制转换。

于 2013-02-07T12:59:04.747 回答
-1

在不将属性放入基类的情况下无法消除强制转换,但您可以使用策略模式来解耦对象创建(有关如何执行此操作的示例,请参见我的问题和答案here )。该示例展示了如何解耦 switch-case,但您可以使用策略模式执行相同的操作来分析上下文的结果,因此您可以隐藏演员表。

public static class A
{
    // please note: some example in related to your question
    // and the strategy pattern provided at the link
    public static P GetI(int a)
    {
        if (a == 1)
            return new I();
        else
            return null; // <- this is important for evaluation
    }

    // can put this method anywhere else, also into another class
    public static P GetO(int a)
    {
        if (a == 2)
            return new O();
        else
            return null; // <- this is important for evaluation
    }

    public static bool Condition(string pci)
    {
        return !string.IsNullOrEmpty(pci); // could eval different states, values
    }
}

然后可以将 as/is 逻辑划分为单独的方法/类,调用类(例如控制器、客户端)不需要知道所有不同的专业化。以下代码显示了如何创建上下文。有关Strategy 和 Context 类的实现细节,请参阅我的帖子。

var strategyI = new Strategy<int, P>(A.Condition, A.GetI);
var strategyO = new Strategy<int, P>(A.Condition, A.GetO);

var strategies = new List<Strategy<int, P>> {strategyI, strategyO};
var context = new Context<int, P>(strategies.ToArray());

var result = context.Evaluate(1); // should return a new I(), with 2 as param a new O()

// once you get the result you can create a new context to work with the result

var strategyWorkI = new Strategy<P, P>(Condition, WorkWithI);
var stragegiesForWork = new List<Strategy<P, P>> {strategyWorkI} // could be more than one
var workContext = new Context<P, P>(strategiesForWork.ToArray());

var p = workContext.Evaluate(result); // does the right thing, but
                                      // no need to know class I here (only P)
if (p == null)
  // seems to be something went wrong, throw? ...         

这是演员阵容的脱钩,但此时您仍然需要演员阵容。

public static P WorkWithI(P p)
{
    var i = p as I; // I would prefer as instead of is (see boxing/unboxing in .net)
    if (i != null)
    {
       // do what you want with concrete class I in i
    }

    return i; // <- this is important for Evaluation
}

无论如何......代码中仍然存在带有is / as的点,但它不会影响您的控制器/管理器等(保持代码灵活和可插入,例如通过依赖注入)

...如果您无法理解我的简短描述,我可以提供一个完整的示例。

于 2013-02-06T21:25:45.057 回答
-1

如果上述所有解决方案都不符合您的要求并且您不想更改基类(从派生类添加属性),那么您不仅应该考虑大多数评论中提到的继承设计......您还应该寻找您的工厂/存储库实施。这个存储库是否在做你需要的事情?为什么不添加 GetI() 和 GetO()?

例如,Linq 允许您使用 .OfType 查询和过滤列表,并且您仅获得类型 T 的元素。

您可以使用类型信息(例如,我在示例中使用泛型)和附加条件添加特定的 Get 方法。

public T Get<T>(string pci, int c)
{
     var a = GetAByPCI(string pci);

     if(a == c)
         return new T();
     else
         // throw exception here.
}

使用通用参数调用 Get 方法。此解决方案的问题是您需要知道调用时的条件,这不处理“特殊构造”(例如,带有 T 参数的构造函数)。无论如何,这也可以通过工厂模式来解决。

I i = PRepository<I>.Get(1, "200"); // create a new I(), only if 1 fits GetAByPCI()
O o = PRepository<O>.Get(2, "123"); // create a new O(), only if 2 fits GetAByPCI()
// can do anything with the concrete type

您还可以使用策略模式将条件从存储库/工厂调用中移出(我在上面的回答中已经提到过)。

也可以在泛型方法后面加上“as”(即使在大多数情况下我不推荐它,我也会向您展示如何)。

public T Get<T>(string pci)
{
     return this.Get(pci) as T; // your method with trying to cast to concrete type
}

这样,您可以获得如下实现:

I i = PRepository<I>.Get("200");
if (i != null)
{
  // do I specific tasks
}
于 2013-02-06T22:59:30.873 回答