当我的方法返回基类型时,如何避免在 c# 中使用 is 运算符

本文关键字:运算符 is 何避免 返回 方法 我的 基类 类型 | 更新日期: 2023-09-27 17:57:56

我有一个抽象的基类P。我有两个继承P类,分别称为 IO

我的存储库中有一个返回PGet()方法。此方法始终返回 IO 的实例,具体取决于值。

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时都必须检查和向下投掷。我觉得我在这里做错了一些根本性的事情,但不确定。

更新:派生类型IO具有需要使用的其他属性,但仅与该特定类型相关。 P具有许多在继承类型中实现的抽象属性。向下浇筑是我根据需要获得混凝土类型的唯一方法?

也许逻辑是完全错误的。

当我的方法返回基类型时,如何避免在 c# 中使用 is 运算符

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

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

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

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

所以也许你的基类中有一个名为 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);
    }
}

我也会考虑使用装饰器模式和组合,而不是使用继承。

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

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

对于 eample 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
}

如果不将属性放入基类中,就不可能消除强制转换,但您可以使用策略模式将对象创建分离(有关如何执行此操作的示例,请参阅我在此处的问题和答案(。该示例显示了如何解耦开关盒,但您可以使用策略模式执行相同的操作来分析上下文的结果,以便隐藏转换。

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逻辑划分为单独的方法/类,并且调用类(例如控制器,客户端(不需要知道所有不同的专用化。以下代码演示如何创建上下文。请参阅我在此处的帖子,了解策略和上下文类的实现详细信息。

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的Point,但它不会影响您的控制器/管理器等(保持代码灵活和可插拔,例如通过依赖注入(

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