将通用方法和重载结合使用

3
我有以下方法:

我有以下方法:

public void Set<T>(IEnumerable<T> records)
{
     foreach (var record in records)
     {
        Set(record);
     }
 }

我希望根据的不同,调用以下任一Set方法之一:
    public void Set(RecordType1 record)
    {
        // Some RecordType1 logic
    }

    public void Set(RecordType2 record)
    {
        // Some logic applicable to RecordType2 only
    }

希望您能看到,我试图允许在运行时推断出可以调用的Set方法。这个“没有工作”(即无法编译,因为它期望RecordType1)。
问题:
如何保持这种结构而不必在将记录发送到Set方法之前进行类型测试?
2个回答

13

为什么不创建一个接口(IRecordType),允许RecordType1RecordType2继承它,然后将每个Set方法的主要逻辑移动到该接口中。

public interface IRecordType
{
    void Set(...);
}
public void Set(IEnumerable<IRecordType> records)
{
    foreach (var record in records)
    {
        record.Set(...);
    }
}

这是一个更易于维护的解决方案。同时,它还允许更好的多态性。

编辑:参考资源:https://msdn.microsoft.com/en-us/library/3b5b8ezk%28v=vs.90%29.aspx

另外,小侧边栏提示:使用 interface 不仅可以共享某些方法,还可以共享属性和事件。如果 RecordType1RecordType2 共享一些常见属性,则可以将这些属性添加到 interface 中,然后在以前需要区分这两个类型以使用这些属性、方法或事件的任何地方使用 IRecordType。同样,任何这些属性、方法或事件内部的代码都允许依赖于对象本身特定的其他属性、方法、事件或字段。这是面向对象语言(如 C#)和多态性的目的。

编辑:由于评论中的讨论结果,我还想增加有关使用面向对象编程方法(interfaceabstract classclass)和 StriplingWarrior 建议的 dynamic 方法之间进行选择的更多信息:

如果您没有访问 RecordType1RecordType2 的实际实现细节,或者由于依赖于 Set(RecordType1)Set(RecordType2) 方法而无法更改应用程序的设计,则可能会发现使用 dynamic 的方法更为有效。还有其他一些选择,我们可能没有想到——您可以随时尝试其中之一。使用 dynamic 方法的缺点是它需要 .NET 4.0。

此外,还有更多考虑事项:如果您可以访问 RecordType1RecordType2 的实现细节,但不能更改 Set(RecordType1)Set(RecordType2) 的定义,则可以修改它们的主体:

public void Set(RecordType1 record)
{
    record.Set(...);
}

public void Set(RecordType2 record)
{
    record.Set(...);
}

这样做可以保留全部的应用程序结构,同时降低代码维护成本并允许多态性。


3
你的回答和原帖中的代码差别很大。原帖中没有在 T 类中调用 Set 方法,而是在当前类中调用。不确定为什么这个回答会得到这么多赞 :\ - Sriram Sakthivel
2
@SriramSakthivel 我知道,OP的方法也不是面向对象编程的好例子。我的解决方案是为OP提供一个更易于维护的项目,不仅仅是回答他的问题,而是解决了一个更深层次的问题。 - Der Kommissar
2
这并不总是可能的(如果类型属于第三方)。在这种情况下,我们需要使用适配器模式或其他某种模式。不过,我喜欢你的回答。+1 :) - Sriram Sakthivel
1
我同意@SriramSakthivel的观点。我没有给你点赞,因为你描述的不是问题的直接答案,而是他的应用程序重新设计。我认为答案首先应该直接回答问题,然后作为附注详细说明其他可能的解决方案。在这方面,StriplingWarrior的帖子比你的更好。请记住,我完全不反对你所说的(我认为这些都是有价值的信息,只是不是问题的答案)。 - julealgon
1
@julealgon 是的,这是我方法不幸的缺点之一,应用程序需要进行广泛的重新设计。像这样的情况使得 StriplingWarrior 的方法成为最优选择。或者其他比较每个对象的 typeof 的情况。然而,这些情况并不满足 OP 的要求:也就是说,决策是在运行时做出的。 - Der Kommissar
显示剩余4条评论

7

如果你确信给定集合中的每个项目都与方法签名匹配,那么可以将这些值强制转换为 dynamic,让运行时决定要绑定到什么上:

 foreach (var record in records)
 {
    Set((dynamic)record);
 }

然而,这种方法容易出错,我建议您仔细考虑您真正想要实现的目标。很可能有更好的模式来满足您的需求。

非常感谢,十分感谢。我一直很想知道“dynamic”在我的代码中的作用。你的回答非常准确,但正如EBrown所指出的那样 - 我显然在整体上的处理方式上存在缺陷。 - user1017882
@DeeMac 不一定,有时候这种解决方案比我提供的更理想,你需要考虑以下几点:如果Set方法中的代码没有描述对object本身的操作,那么你应该使用这种方法。然而,我怀疑你的Set方法在很大程度上描述了对对象本身的操作,因此它应该放在它所操作的对象内部。 - Der Kommissar
@DeeMac 此外,正如SriramSakthivel在我的答案评论中建议的那样:如果您无法访问RecordType1或RecordType2的源代码,则使用此方法会更好,因为它不需要调整/修改对象定义本身。 - Der Kommissar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接