我有一个情况,即给出了所有从同一基类得出的对象的集合。如果我迭代集合并检查每个项目的类型,我可以看到该对象是派生类型的,然后相应地处理。我想知道的是,除了我已经在做什么外,还有一种更简单的方法来执行派生类型的检查。通常不需要代码重复,所以我当前的方法对我来说似乎有点不合时宜。
class A {}
class B : A {}
class C : A {}
class D : C {}
class Foo
{
public List<A> Collection { get; set; }
}
class Bar
{
void Iterate()
{
Foo f = new Foo();
foreach(A item in f.Collection)
{
DoSomething(a);
}
}
void DoSomething(A a)
{
...
B b = a as B;
if(b != null)
{
DoSomething(b);
return;
}
C c = a as C;
if(c != null)
{
DoSomething(c);
return;
}
D d = a as D;
if(d != null)
{
DoSomething(d);
return;
}
};
void DoSomething(B a){};
void DoSomething(C a){};
void DoSomething(D a){};
}
我正在使用一个Web服务,每个Web服务都必须具有相同的结果类型。
class WebServiceResult
{
public bool Success { get; set; }
public List<Message> Messages { get; set; }
}
class Message
{
public MessageType Severity { get; set; } // Info, Warning, Error
public string Value { get; set; } //
}
class InvalidAuthorization: Message
{
// Severity = MessageType.Error
// Value = "Incorrect username." or "Incorrect password", etc.
}
class InvalidParameter: Message
{
// ...
}
class ParameterRequired: InvalidParameter
{
// Severity = MessageType.Error
// Value = "Parameter required.", etc.
public string ParameterName { get; set; } //
}
class CreatePerson: Message
{
// Severity = MessageType.Info
// Value = null
public int PersonIdentifier { get; set; } // The id of the newly created person
}
目标是我们可以根据需要将尽可能多的不同类型的消息返回客户端。Callee无需收到单个消息,而是可以在一次旅行中知道他们的所有错误/成功,并消除字符串从消息中解析特定信息。
i最初是关于使用仿制药的,但是由于Web服务可能具有不同的消息类型,因此该集合被扩展以使用基本消息类。
可能可以将DoSomething
移至A
并让每个子类提供自己的实现:
public abstract class A
{
abstract void DoSomething();
}
void Iterate()
{
Foo f = new Foo();
foreach(A item in f.Collection)
{
item.DoSomething();
}
}
一个想法是在基类或接口上使用一般约束。
public class MyClass<T> where T : BaseClass, IInterface
{
public void executeCode<T>(T param) {};
}
因此,MyClass<T>
仅采用某种类型,executeCode
将了解哪些方法已公开,并且可以对传递对象的数据执行哪些操作。这避免了铸造的需求,因为您指定了必须遵循的合同。
typeof(ParentClass).IsAssignableFrom(typeof(ChildClass));
返回true是可能的。
也可以这样:
typeof(ParentClass).IsAssignableFrom(myObject.GetType());
但是在您的示例中,您实际上调用了每种对象类型的方法。因此,无论如何您都需要演员,除非您不介意重构没有超负荷的收集。
如果要保持超负荷:
这样的东西foreach(A item in f.Collection)
{
Type itemType = item.GetType();
if (typeof(B).IsAssignableFrom(itemType)
DoSomethingB(item);
else if (typeof(C).IsAssignableFrom(itemType)
DoSomethingC(item);
//...
}
编辑:我喜欢更多李的答案。将虚拟/覆盖函数添加到类类型将是一个更好的设计,并且更易于处理,除非dosomething在类中确实没有什么可做的。
Lee是正确的。只需让项目决定该怎么办。它知道它是最好的,因此知道该怎么做。您甚至可以通过不抽象而是虚拟的,甚至可以提供一些标准的实现,如果它与A中的实现相同。但是请注意,编译器不会要求实施。
public class A
{
public virtual DoSomething(){"What A needs doning!"}
}
public class B : A
{
public override DoSomething() {"What B needs doing!"}
}
另一种方法是使用接口。
public interface IAinterface
{
void DoSomething();
}
public class A : IAinterface
{
void DoSomething(){...}
}
public class B : IAinterface
{
void DoSomething(){...}
}
这更像是Lees的建议,尽管接口和抽象基类在背景中的作用有所不同。
我通常更喜欢上层,因为我通常倾向于给基类一些标准的行为,并且仅在有不同的东西时才能实现派生的类。