Конструкторы с одинаковым типом аргумента - PullRequest
7 голосов
/ 28 августа 2008

У меня есть объект Person с двумя конструкторами - один принимает int (personId), другой - строку (logonName). Я хотел бы другой конструктор, который принимает строку (badgeNumber). Я знаю, что это невозможно, но, похоже, это обычная ситуация. Есть ли изящный способ справиться с этим? Я полагаю, это относится к любому перегруженному методу. Код:

public class Person
{
    public Person() {}

    public Person(int personId)
    {
        this.Load(personId);
    }

    public Person(string logonName)
    {
        this.Load(logonName);
    }

    public Person(string badgeNumber)
    {
        //load logic here...
    }

... и т.д.

Ответы [ 12 ]

18 голосов
/ 28 августа 2008

Вы могли бы вместо этого использовать фабричные методы?

public static Person fromId(int id) {
    Person p = new Person();
    p.Load(id);
    return p;
}
public static Person fromLogonName(string logonName) {
    Person p = new Person();
    p.Load(logonName);
    return p;
}
public static Person fromBadgeNumber(string badgeNumber) {
    Person p = new Person();
    // load logic
    return p;
}
private Person() {}
7 голосов
/ 28 августа 2008

Вы можете рассмотреть возможность использования пользовательских типов.

Например, создать классы LogonName и BadgeNumber.

Тогда ваши объявления функций выглядят как ...

public Person(LogonName ln)
{
    this.Load(ln.ToString());
}

public Person(BadgeNumber bn)
{
    //load logic here...
}

Такое решение может дать вам хорошее место для сохранения бизнес-логики, которая определяет формат и использование этих строк.

2 голосов
/ 28 августа 2008

Если вы используете C # 3.0, вы можете использовать Инициализаторы объектов :

public Person()
{
}

public string Logon { get; set; }
public string Badge { get; set; }

Вы бы назвали конструктор так:

var p1 = new Person { Logon = "Steve" };
var p2 = new Person { Badge = "123" };
2 голосов
/ 28 августа 2008

У меня есть четыре варианта, три из которых уже были названы другими:

  1. Пройдите заводской маршрут, как предложили несколько других здесь. Одним из недостатков этого является то, что у вас не может быть последовательного именования из-за перегрузки (иначе у вас возникнет та же проблема), так что это внешне менее чисто. Другой, более крупный недостаток заключается в том, что он исключает возможность размещения непосредственно в стеке. Все будет распределено в куче, если вы воспользуетесь этим подходом.

  2. Пользовательские обертки объектов. Это хороший подход, и я бы порекомендовал его, если вы начинаете с нуля. Если у вас много кода с использованием, например, значков в качестве строк, то переписывание кода может сделать этот параметр нежизнеспособным.

  3. Добавьте перечисление к методу, определяющее, как обрабатывать строку. Это работает, но требует, чтобы вы переписали все существующие вызовы, чтобы включить новое перечисление (хотя при желании вы можете указать значение по умолчанию, чтобы избежать этого).

  4. Добавить фиктивный параметр, который не используется, чтобы различать две перегрузки. например Прикрепите bool к методу. Этот подход используется стандартной библиотекой в ​​нескольких местах, например std::nothrow является фиктивным параметром для operator new. Недостатки этого подхода в том, что он уродлив и не масштабируется.

Если у вас уже есть большая база существующего кода, я бы рекомендовал либо добавить перечисление (возможно, со значением по умолчанию), либо добавить фиктивный параметр. Ни то, ни другое не красиво, но оба довольно просты в модернизации.

Если вы начинаете с нуля или у вас мало кода, я бы порекомендовал пользовательские оболочки объектов.

Методы фабрики могут быть полезны, если у вас есть код, который интенсивно использует необработанные строки badge / logonName, но не использует класс Person.

1 голос
/ 28 августа 2008

Как уже было предложено, в этом случае лучше использовать пользовательские типы.

1 голос
/ 28 августа 2008

Вы можете использовать статический фабричный метод:

public static Person fromLogon(String logon) { return new Person(logon, null); }
public static Person fromBadge(String badge) { return new Person(null, badge); }
1 голос
/ 28 августа 2008

У вас не может быть двух разных конструкторов / методов с одной и той же сигнатурой, иначе как компилятор может определить, какой метод запустить.

Как сказал Зак , я бы подумал о создании класса "options", в котором вы могли бы на самом деле передавать параметры, содержащиеся в пользовательском типе. Это означает, что вы можете передавать столько параметров, сколько захотите, и делать то, что вам нравится, с опциями, просто будьте осторожны, вы не создадите монолитный метод, который пытается сделать все ..

Либо так, либо голосуйте за фабричный шаблон ..

1 голос
/ 28 августа 2008

Это не сработает. Вы можете создать класс с именем BadgeNumber, который обернет строку, чтобы избежать этой неоднозначности.

1 голос
/ 28 августа 2008

номер

Вы можете рассмотреть поле флага (enum для удобства чтения), а затем попросить конструктор использовать htat, чтобы определить, что вы имели в виду.

0 голосов
/ 28 августа 2008

Как насчет ...

public Person(int personId)
{
    this.Load(personId);
}

public Person(string logonName)
{
    this.Load(logonName);
}

public Person(Object badgeNumber)
{
    //load logic here...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...