当我的方法返回基类型时,如何避免在 c# 中使用 is 运算符
本文关键字:运算符 is 何避免 返回 方法 我的 基类 类型 | 更新日期: 2023-09-27 17:57:56
我有一个抽象的基类P
。我有两个继承P
类,分别称为 I
和 O
。
我的存储库中有一个返回P
的Get()
方法。此方法始终返回 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
具有许多在继承类型中实现的抽象属性。向下浇筑是我根据需要获得混凝土类型的唯一方法?
也许逻辑是完全错误的。
拥有基类的全部意义在于避免处理子类本身。只需向基类添加一个抽象方法,并在子类中实现它。这样,调用代码将简单地调用该方法,而无需执行任何强制转换。这是基本的多态性。
你的意图是什么?看来你没有正确使用继承。如果从方法返回 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,但它不会影响您的控制器/管理器等(保持代码灵活和可插拔,例如通过依赖注入(
。如果您无法理解我的简短描述,我可以提供一个完整的示例。