У меня есть объект 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...
}
...так далее.





Вы можете рассмотреть возможность использования нестандартных типов.
Например, создайте классы LogonName и BadgeNumber.
Тогда объявления ваших функций выглядят так ...
public Person(LogonName ln)
{
this.Load(ln.ToString());
}
public Person(BadgeNumber bn)
{
//load logic here...
}
Такое решение может дать вам хорошее место для хранения бизнес-логики, которая управляет форматом и использованием этих строк.
Нет.
Вы можете рассмотреть поле флага (перечисление для удобства чтения), а затем попросить конструктор использовать htat, чтобы определить, что вы имели в виду.
Единственное, что я могу придумать, чтобы справиться с тем, что вы хотите сделать, - это иметь params, один, который описывает тип параметра (перечисление с LogonName, BadgeNumer и т. д.), А второй - значение параметра.
Это не сработает. Вы можете подумать о создании класса с именем BadgeNumber, который обертывает строку, чтобы избежать этой двусмысленности.
Возможно, вы могли бы использовать вместо этого фабричные методы?
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() {}
У вас не может быть двух разных конструкторов / методов с одной и той же сигнатурой, иначе как компилятор может определить, какой метод запускать.
Как Зак сказал, я бы подумал о создании класса «параметров», где вы могли бы фактически передавать параметры, содержащиеся в настраиваемом типе. Это означает, что вы можете передавать столько параметров, сколько захотите, и делать с ними то, что вам нравится, только будьте осторожны, вы не создадите монолитный метод, который пытается делать все.
Либо так, либо голосуйте за заводской образец ..
Вы можете использовать статический фабричный метод:
public static Person fromLogon(String logon) { return new Person(logon, null); }
public static Person fromBadge(String badge) { return new Person(null, badge); }
Вы можете переключиться на шаблон заводского стиля.
public class Person {
private Person() {}
public static PersonFromID(int personId)
{
Person p = new Person().
person.Load(personID);
return p;
this.Load(personId);
}
public static PersonFromID(string name)
{
Person p = new Person().
person.LoadFromName(name);
return p;
}
...
}
Или, как предлагается, используйте настраиваемые типы. Вы также можете взломать что-нибудь с помощью дженериков, но я бы не рекомендовал это для удобства чтения.
Как и было предложено, в этом случае лучше всего использовать нестандартные типы.
Как насчет ...
public Person(int personId)
{
this.Load(personId);
}
public Person(string logonName)
{
this.Load(logonName);
}
public Person(Object badgeNumber)
{
//load logic here...
}
Я могу придумать четыре варианта, три из которых уже были названы другими:
Пойдите по фабричному маршруту, как предлагают здесь несколько других. Одним из недостатков этого является то, что у вас не может быть последовательного именования через перегрузку (иначе у вас возникнет та же проблема), поэтому он внешне менее чист. Другой, более серьезный недостаток заключается в том, что он исключает возможность выделения памяти непосредственно в стеке. Если вы воспользуетесь этим подходом, все будет размещено в куче.
Пользовательские оболочки объектов. Это хороший подход, и я бы рекомендовал его, если вы начинаете с нуля. Если у вас много кода, использующего, например, значки в виде строк, переписывание кода может сделать этот вариант нежизнеспособным.
Добавьте в метод перечисление, указав, как обрабатывать строку. Это работает, но требует, чтобы вы переписали все существующие вызовы, чтобы включить новое перечисление (хотя вы можете указать значение по умолчанию, если хотите, чтобы избежать некоторых из них).
Добавьте фиктивный параметр, который не используется, чтобы различать две перегрузки. например Прикрепите bool к методу. Этот подход используется стандартной библиотекой в нескольких местах, например. std::nothrow - это фиктивный параметр для operator new. Недостатки этого подхода в том, что он уродлив и не масштабируется.
Если у вас уже есть большая база существующего кода, я бы рекомендовал либо добавить перечисление (возможно, со значением по умолчанию), либо добавить фиктивный параметр. Ни один из них не является красивым, но оба варианта довольно просто модернизировать.
Если вы начинаете с нуля или имеете небольшой объем кода, я бы порекомендовал пользовательские оболочки объектов.
Фабричные методы были бы вариантом, если у вас есть код, который активно использует необработанные строки badge / logonName, но не использует класс Person.
Если вы используете 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" };
В зависимости от ограничений вашего бизнеса:
public class Person
{
public string Logon { get; set; } = "";
public string Badge { get; set; } = "";
public Person(string logon = "", string badge = "") {}
}
// Use as follow
Person p1 = new Person(logon:"MylogonName");
Person p2 = new Person(badge:"MyBadge");
Это однострочный (однострочный) оператор, но он упускает из виду преимущество конструкторов - конструкторы требовать предоставляют некоторые параметры, а инициализаторы - это просто общедоступные переменные.