我有一个名为Block的超类和另外3个子类。我要实现的类包含3个重载函数,每个重载函数将子类之一的对象作为参数。使用这些功能之一时,我只有一个Block对象(superClass中的一个对象)。我的问题是选择要调用的函数的最干净方法是什么。
到目前为止,我所做的是如果对象类型的条件随后进行了强制转换。但似乎不干净。
这些是重载的函数。
public void WriteBlock(TableBlock block) { }
public void WriteBlock(TextBlock block) { }
public void WriteBlock(ListBlock block) { }
这是我要实现的功能。
public void WriteBlocks(List<Block> blocks)
{
BlockWriter w = new BlockWriter();
foreach (var block in blocks)
{
w.WriteBlock(block);
}
}
请注意,我无权访问Blocks类。
答案 0 :(得分:1)
不。鉴于此:
public void WriteBlocks(List<Block> blocks)
对于列表中的每个项目,编译器唯一了解的是它是Block
。这就是所有应该知道的。这就是使多态成为可能的原因。从Block
继承的任何类都可以,但是在这种情况下,这些区别并不重要。
但是,如果编译器仅知道每个项目都是Block
,就无法知道任何单个项目可能是TableBlock
,TextBlock
还是其他继承类型。如果在编译时不知道运行时类型是什么,则甚至不知道该特定类型是否存在重载。
假设您要尝试执行的操作可以编译,因为对于从Block
继承的每个类型,您都有重载。如果添加了新类型class PurpleBlock : Block
,并且没有重载,将会发生什么或应该发生什么?应该不再仅仅因为添加了新类型而编译了吗?
如果调用 WriteBlocks
的方法知道列表中的Block
是哪种类型,那么它可以提供该信息:
public void WriteBlocks<TBlock>(List<TBlock> blocks) where TBlock : Block
现在您可以调用WriteBlock<TextBlock>(listOfTextBlocks)
,编译器将知道列表中的每个项目都是TextBlock
,而不仅仅是Block
。
然后,BlockWriter
也必须是通用的,以便对不同类型的Block
可以有不同的实现。注入它可能更有意义。无论哪种方式,您都可能会认为您已经“解决”了问题。如果调用WriteBlocks
的类“知道” Block
的类型,则该方法确定要使用的BlockWriter
的类型可能更有意义。
如您的评论中所述,列表可能包括不同类型的Block
,而不仅仅是一种。这需要根据BlockWriter
的类型返回特定的Block
的方法或类。这意味着运行时类型检查,这不是理想的选择,但是如果将其放在一个位置也不会太糟糕。
这是一个简单的例子:
public class BlockWriterFactory
{
public BlockWriter GetBlockWriter(Block block)
{
if (block is TextBlock)
return new TextBlockWriter();
if (block is TableBlock)
return new TableBlockWriter();
if (block is ListBlock)
return new ListBlockWriter();
// this could be a "null" class or some fallback
// default implementation. You could also choose to
// throw an exception.
return new NullBlockWriter();
}
}
(NullBlockWriter
只是当您调用其Write
方法时不执行任何操作的类。)
这种类型检查并不理想,但至少可以将其隔离为一个类。现在,您可以创建(或注入)工厂的实例,并调用GetBlockWriter
,而该方法中的其余代码仍然不会“知道”关于不同类型的Block
或BlockWriter
。
BlockWriter w = new BlockWriter();
将成为
BlockWriter w = blockWriterFactory.GetBlockWriter(block);
...然后其余部分将保持不变。
这是最简单的工厂示例。还有其他创建此类工厂的方法。您可以将所有实现存储在Dictionary<Type, BlockWriter>
中,然后尝试使用block.GetType()
检索实例。
答案 1 :(得分:1)
是的,可以使用允许这种操作的dynamic
类型。
如果您使用:
foreach (var block in blocks)
{
w.WriteBlock(block as dynamic);
}
它应该调用预期的WriteBlock
重载。
这在另一个问题中进行了更详细的描述:https://stackoverflow.com/a/40618674/3195477
还有此处:method overloading and dynamic keyword in C#。
注意事项:
我不确定是否有任何与此类型的dynamic
“ cast”相关的运行时惩罚。
每当我看到这种模式时,我都会想知道是否可以改进类层次结构。也就是说,WriteBlock
类别中的所有内容实际上是否应该在Block
类中移动?那可能是“更加多态的”。另外,使用dynamic
可能会有些脆弱,因为您可以添加新的Block
派生类型,而忘记为它们重载WriteBlock
,这可能会导致错误。 (这更多的证据表明,WriteBlock
中的某些本身应该被合并到Block
类中。)
例如,将一个virtual
PrepareForWriting()
添加到基类Block
中,这将返回一个BlockWritable
。然后,您只需要一个WriteBlock(BlockWritable data)
即可进行写作。 BlockWritable
可以是字符串,Json,XML等。这假设您能够修改Block
类(看来您不能)。