重载具有相同数量的参数和相同类型的方法

时间:2018-07-20 04:58:08

标签: c#

在这里寻找一些设计建议。我已经碰到过几次这个问题了,直到现在我的方法还是不够完善。

例如,我要创建一个名为 GetConversation 的方法。现在,无论您为其赋予 id 还是其他 address 参数, GetConversation 都将返回相同的数据。只是一个偏好问题,或者您拥有的任何可用数据都会使它更加灵活和易于使用。很好,但是我遇到的问题是这两个都是字符串参数。理想情况下,看起来像这样,

public object GetConversation(string id)
{
    // Get Conversation by id
}

public object GetConversation(string address)
{
    // Get Conversation by address
}

现在,由于模棱两可,这当然会失败。当用户深入到 GetConversation 时,编译器不知道要访问哪个功能。

这将我带到了第二种方法,即灵活的解决方案,

public object GetConversation(string id, string address)
{
    // Get Conversation by id or address
    if (id != null)
    {
        // Get by id
    }
    else if (address != null)
    {
        // Get by address
    }

    return null;
}

但是现在我开始倾向于总是先按ID搜索。不一定是一件坏事,但我觉得这不是最有凝聚力的解决方案,我必须在我的支票之外明确设置两个支票都失败的收益。

对更好的方法有何想法?

5 个答案:

答案 0 :(得分:4)

在这种特定情况下,我将方法重命名为GetConversationByIdGetConversationByAddress,以避免产生歧义。

如果情况稍有不同,并且这些方法是具有不同实现的真正的重载,那么我将使用strategy patternabstract factory来创建策略。

答案 1 :(得分:2)

您可能希望考虑的另一种方法是引入新类型 1 。因此,您将拥有Address类型和Id类型。

至少,您希望这些新类型支持(显式)到string的转换,并实现所有期望的相等和比较函数 2 。然后,您可以将这些类型用于此功能,并且1)允许使用,因为现在这些类型有所不同,并且2)现在可以清楚地看到调用方如何弄清楚他们正在调用哪个类型:

var conv = thing.GetConversation((Address)"here");

现在,如果这是您唯一使用这些类型的地方,那么似乎不值得付出努力-但是这些类型还有更多用途吗?除了Id以外,Addressstring还具有更多的结构吗? (当然,如果事实证明Address必须始终是有效的Uri,那么事实证明我们并不需要做所有这些工作,因为有人已经写过该类给我们)。如果是这样,我们可以考虑使用静态TryParse方法来允许用户安全地测试字符串。

您可以使用这些类型的场所越多,当期望id时偶然通过address的次数就越少,反之亦然。

您还可以考虑希望通过包装器公开的基础类型的哪些操作。当然,串联和分割 strings 是有意义的,但是将两个id或两个address串联在一起是否有意义? (或更奇怪的是,将idaddress连接在一起?)。因此,您只公开了对这些新类型有意义的操作,并且正在引导消费者减少可能的滥用。通过将显式运算符提供回string,如果消费者确实确实需要做一些您认为没有用的操作,那么总会有一个“释放阀”。


1 引入此类包装程序会产生一些开销。因此,我不建议在热路径上对性能敏感的内容执行此操作,但是在大多数情况下,这将不适用。

2 是的,编写所有常用样板来实现IEquatable<T>IComparable<T>确实很繁琐,但是您可以在VS中对此进行摘要。

答案 2 :(得分:1)

很明显,您不能重载相同签名的方法。

如果您要编译时类型安全,我建议使用这两种方法之一。

(1)

public object GetConversationById(string id)
{
    // Get by id
}

public object GetConversationByAddress(string address)
{
    // Get by address
}

(2)

public object GetConversation(ConversationSource source, string parameter)
{
    switch (source)
    {
        case ConversationSource.Id :
            // Get by id
            break;
        case ConversationSource.Address :
            // Get by address
            break;          
    }
    return null;
}

public enum ConversationSource
{
    Id,
    Address,
}

您将像这样使用它们:

(1)

var conversation1 = GetConversationById("A123");
var conversation2 = GetConversationByAddress("1 Somewhere St");

(2)

var conversation1 = GetConversation(ConversationSource.Id, "A123");
var conversation2 = GetConversation(ConversationSource.Address, "1 Somewhere St");

对我来说,选项(1)更干净。

答案 3 :(得分:0)

这取决于您的设计以及如何存储对话(列表,数据库,其他服务等)以及所使用的内容,但是您可以定义一个“通用”方法来通过使用谓词({{ 1}})作为参数。

Func<Conversation>

因此该方法的调用者可以按以下方式使用它

private List<Conversation> _conversations;

public Conversation FindConversation(Func<Conversation> predicate) {
  return _conversations.FirstOrDefault(predicate);
}

然后,您只有一种方法可以进行对话,并且如果需要通过其他参数查找对话,则不需要定义其他方法。

如果将转换存储在数据库中,则应使用FindConversation(p=>p.Id == 123); //or FindConversation(p=>p.Address == "street"); //or FindConversation(p=>p.Id = 123 && p.Address == "street"); 作为谓词,以便数据库提供程序(IQueryable)可以将谓词转换为相应的查询语句(sql等)

答案 4 :(得分:0)

您可以选择Switch Case。它将同时适用于两种以上类型。

public object GetConversation(string param, string value)
{
    switch(param)
    {
        case "id":
            // ...
            break;
        case "address":
            // ...
            break;
        default:
            break;
    }

    return null;
}

GetConversation("id", id);
GetConversation("address", address);