我正在尝试优化我的代码的某个部分,这恰好是在一个紧密的性能循环中。主要是我正在努力学习将来可以应用的新事物。我的实现非常冗长,所以我将举例说明我想要实现的目标。
我的问题与此有关:C# 'is' operator performance,尤其是所选择的答案。
假设我有一个A类。我还有一个B类,它是从A派生的。我有一个A类列表(包含A和B类型的混合)。在我处理这些项目的方法中,我希望根据对象的实际类型实现某种行为(不确定这是否是正确的说法。无论我说错什么,请纠正我。)
void Process(A item)
{
if (item is A)
{
DoBehavior((A)item); //I know the cast is redundant here, I'm just leaving
//it here for my explanation.
}
else if (item is B)
{
DoBehavior((B)item);
}
}
void DoBehaviour(A item)
{
//perform necessary behaviour for type A
}
void DoBehaviour(B item)
{
//perform necessary behaviour for type B
}
这是我目前的做法。请注意,我遍历A类型的列表,其中包含A和B' s。此外,如果您觉得我没有提供足够的代码来澄清情况,我很乐意扩展。
在上面发布的问题C# 'is' operator performance中,我了解到我可以改变结构以使用" as"运算符,并完全摆脱显式演员。
B bItem = item as B;
if (bItem != null)
{
DoBehavior(bItem);
}
这一切都很好,但是,实际上我不仅有A和B,我还有C,D等等,所有这些都来自基类A.这将导致其中许多if语句,它们必须嵌套以获得最佳性能:
B bItem = item as B;
if (bItem != null)
{
DoBehavior(bItem);
}
else
{
C cItem = item as C;
if (cItem != null)
{
DoBehavior(cItem);
}
else
{
//and so on.
}
}
现在这很难看。我喜欢编写整洁,优雅的代码,但我做得非常糟糕(这常常导致我浪费时间试图让事情看起来更好一些)。
我希望这个问题不是广泛的,但首先我想知道在获取类型时是否有更优化和干净的解决方案,以便执行相关行为。如果没有,是否有一种更清洁的方式来使用这些'作为'运营商比这样嵌套吗?
我想一种替代方法是将行为移动到基类A中,然后为每个派生类重写它。然而,在更高的思维意义上,我的这种特殊情况下的行为不是A类(或它的孩子)的行为,相反,它是一些在其上行动/表现的外部类(它会表现出来)每种类型都不同)。如果没有更好的方法,我会强烈考虑实施它,正如我现在解释的那样 - 但我想就此提出一些专家意见。
我试图保持这一点,可能会留下太多细节。如果是这种情况,请告诉我。
答案 0 :(得分:2)
这不是多态性的全部意义吗?一种根据其类型具有不同行为的方法。而且我很确定这会比“打字机”更快 如果需要,您还可以使用函数重载(用于外部处理),请参阅下面的测试程序:
using System;
using System.Collections.Generic;
public class A
{
public String Value
{
get;
set;
}
public A()
{
Value = "A's value";
}
public virtual void Process()
{
// Do algorithm for type A
Console.WriteLine("In A.Process()");
}
}
public class B : A
{
public int Health
{
get;
set;
}
public B()
{
Value = "B's value";
Health = 100;
}
public override void Process()
{
// Do algorithm for type B
Console.WriteLine("In B.Process()");
}
}
public static class Manager
{
// Does internal processing
public static void ProcessInternal(List<A> items)
{
foreach(dynamic item in items)
{
item.Process(); // Call A.Process() or B.Process() depending on type
ProcessExternal(item);
}
}
public static void ProcessExternal(A a)
{
Console.WriteLine(a.Value);
}
public static void ProcessExternal(B b)
{
Console.WriteLine(b.Health);
}
public static void Main(String[] args)
{
List<A> objects = new List<A>();
objects.Add(new A());
objects.Add(new B());
ProcessInternal(objects);
}
}
请注意,这只适用于.Net 4.0!
答案 1 :(得分:2)
我强烈建议您通过编程接口而不是引用具体类来避免使用“if..else if..else if ..”路径。
要实现此目的,首先要使Process()
方法不知道其参数的类型。该参数可能最终会成为像IDoSomething
这样的界面。
接下来,实施Process()
,以便它不会直接调用DoSomething()
。您将不得不在较小的代码块中中断DoSomething()
,这些代码将被移动到IDoSomething
方法的特定实现中。 Process()
方法会盲目地调用这些方法 - 换句话说,将IDoSomething
合约应用于某些数据。
这可能比较复杂DoSomething()
更令人厌烦,但您可以更好地分离关注点,并且会Process()
“打开”IDoSomething
兼容类型,甚至不再写else
。
答案 2 :(得分:2)
我发现的最佳解决方案是使用Double-Dispatch / Visitor模式。我描述了一种情况,其中基类A是抽象的,具体的类B和C继承自A.另外,通过在基类A抽象中创建DoBehavior方法,我们强迫自己在任何需要它的地方进行实现。 ,所以如果我们扩展它以添加更多类型,我们不会忘记添加它的DoBehavior方法(似乎不太可能忘记,但这种行为可能对你添加的其他新类型无关紧要,可能会被忽视 - 特别是如果有很多这些行为模式)
interface IVisitor
{
void DoBehavior(B item);
void DoBehavior(C item);
}
abstract class A
{
abstract void DoBehavior(IVisitor visitor);
}
class B : A
{
override void DoBehavior(IVisitor visitor)
{
//can do some internal behavior here
visitor.DoBehavior(this); //external processing
}
}
class C : A
{
override void DoBehavior(IVisitor visitor)
{
//can do some internal behavior here
visitor.DoBehavior(this); //external processing
}
}
class Manager: IVisitor //(or executor or whatever. The external processing class)
{
public static void ProcessAll(List<A> items)
{
foreach(A item in items)
{
item.DoBehavior(this);
}
}
void DoBehavior(B item)
{
}
void DoBehavior(C item);
{
}
}
感谢大家的贡献。学到了很多,并从大家那里得到了一些好主意(如果你遇到类似的情况,那么阅读所有答案是值得的。)
答案 3 :(得分:1)
一个简单的解决方案是在基类中添加一个指定类类型的字段。
class A
{
// Alternative
string typeName = this.GetType().Name;
public virtual string TypeName { get { return typeName; } }
public virtual string GetTypeName() { return "A"; }
}
class B : A
{
public override string GetTypeName() { return "B"; }
}
class C : A
{
public override string GetTypeName() { return "C"; }
}
class Executer
{
void ExecuteCommand(A val)
{
Console.WriteLine(val.GetType().Name);
switch (val.GetTypeName())
{
case "A": DoSomethingA(val as A); break;
case "B": DoSomethingB(val as B); break;
case "C": DoSomethingC(val as C); break;
}
}
private void DoSomethingC(C c)
{
throw new NotImplementedException();
}
private void DoSomethingB(B b)
{
throw new NotImplementedException();
}
private void DoSomethingA(A a)
{
throw new NotImplementedException();
}
}
你真的不需要使用字符串,但我更喜欢使用整数的选项,原因很简单,你不能在同一个命名空间中声明同名的2个类,因此如果你总是返回名字类,你有一个自动反冲突机制。