Какой самый элегантный способ перегрузить конструктор / метод? - PullRequest
5 голосов
/ 02 февраля 2010

Перегрузка конструкторов и методов кажется грязной , т.е. просто дифференцируя их по порядку и количеству параметров. Разве нет способа, возможно, с помощью generics , чтобы сделать это чисто, чтобы, даже если у вас просто один параметр (например, строка idCode / string status), вы все равно могли их дифференцировать

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            TheForm tf1 = new TheForm("online", DateTime.Now);
            TheForm tf2 = new TheForm(DateTime.Now, "form1");
        }
    }

    public class TheForm
    {
        public TheForm(string status, DateTime startTime)
        {
           //...
        }

        public TheForm(DateTime startTime, string idCode)
        {
           //...
        }
    }
}

Ответы [ 13 ]

13 голосов
/ 02 февраля 2010

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

8 голосов
/ 02 февраля 2010

Вы можете рассмотреть возможность использования Fluent Builder для класса, хотя это больше работы. Это позволит вам написать что-то вроде этого:

var form = new TheFormBuilder().WithStatus("foo").WithStartTime(dt).Build();

Это более явно, но не обязательно лучше. Это определенно больше работы.

В C # 4 вы можете дополнительно написать имена параметров при вызове конструктора:

var form = new TheForm(status: "Foo", startTime: dt);
6 голосов
/ 02 февраля 2010

Новая функция инициализации объекта .NET 3.0 более гибкая, чем перегруженный конструктор. Вот простой пример:

public class Item
{
    public string Name {get; set;}
    public int Index {get; set;}
    public string Caption {get; set;}
} 

Как написано сейчас, мы можем сделать следующее в коде:

var x = new item {Name=”FooBar”};
var x = new item {Name=”FooBar”, Index=”1”, Caption=”Foo Bar”};

Я бы добавил перегруженный конструктор в класс Item, только если я хочу добавить функциональность во время инициализации свойства. Например:

public class Item
{
    public Item() {}

    public Item(string name)
    {
        Name = name;
        Caption = name; //defaulting name to caption
    }

    public Item(string name, int index) : this(name)
    {
        Index = index;
    }

    public Item(string name, int index, string caption) : this(name, int)
    {
        Caption = caption;
    }

    public string Name {get; set;}
    public int Index {get; set;}
    public string Caption {get; set;}
} 

Примечание. Если бы это был дочерний класс, я мог бы быть связан с родительским конструктором с ключевым словом base.

Если я пишу класс «конфигурации», я использую Fluent Methods вместо перегруженных конструкторов.

Например, если я добавил эти методы в класс Item:

public Item WithName(string name)
{
    Name = name;
    return this;
}
public Item WithIndex(int index)
{
    Index = index;
    return this;
}
public Item WithCaption(string caption)
{
    Caption = caption;
    return this;
}

Я мог бы написать такой код:

var x = new Item().WithName(“FooBar”).WithIndex(“99”).WithCaption(“Foo Bar”); 
5 голосов
/ 02 февраля 2010

Единственный способ, которым я могу придумать, как дифференцировать конструкцию с одним параметром заданного типа, - это использовать фабричный метод, отличный от экземпляра, либо для самого типа, либо для фабричного класса.

например. (на самом типе)

(непроверенные)

public class TheForm 
{ 
    public static TheForm CreateWithId(string idCode)
    {
    }

    public static TheForm CreateWithStatus(string status)
    {
    }
} 
2 голосов
/ 02 февраля 2010

До создания Fluent нам иногда удавалось обойтись объектами параметров или объектами настройки:

public class FormSetup {
  public string Status ...
  public string Id ...
}


var frm = new MyForm(new FormSetup { Status = "Bla", ... });
1 голос
/ 02 февраля 2010

Вот пример:

Timespan.FromMilliseconds(double)
Timespan.FromSeconds(double)
Timespan.FromMinutes(double)
Timespan.FromHours(double)
Timespan.FromDays(double)
1 голос
/ 02 февраля 2010

Мы используем свойства вместо перегрузки конструкторов, это довольно чисто и легко реализовать:

public class x {
  public string prop1 {get;set;}
  public DateTime prop2 {get;set;}
  ...
}

и затем заполните только те свойства, которые вам нужны во время создания экземпляра (и / или позже)

var obj = new x() {
  prop1 = "abc",
  prop2 = 123
};

Преимущество этого в том, что он работает с .Net 3.5 и дает действительно четкое представление о том, что устанавливается. (в отличие от var obj = new x("abc", 123, true, false, ... etc), где вы должны угадать значение каждого значения, которое может стать очень проблематичным при многих перегрузках)

1 голос
/ 02 февраля 2010

В C # (как и во многих других языках программирования) в этом случае вы должны использовать Factory Methods . Примерно так:

class TheForm
{
  public static TheForm CreateFromId(string idCode);
  public static TheForm CreateFromStatus(string status);
}

или художественные параметры:

class TheForm
{
  public TheForm(string idCode, int);
  public TheForm(string status);
}

Или вы можете использовать Eiffel;):

class THE_FORM create
   make_from_id, make_from_status
feature
  ...
end
1 голос
/ 02 февраля 2010

Используйте вспомогательные классы инициализации для передачи семантики ваших перегрузок.

Так, например, определить

public class TheForm
{
    public class TheForm(ById initializer)
    {
        //...
    }

    public class TheForm(ByStatus initializer)
    {
        //...
    }

    // ... 

    public class ById
    {
        public ById(DateTime startTime, string idCode)
        // ...
    }

    public class ByStatus
    {
        public ByStatus(string status, DateTime startTime)
        // ...
    }
}

Однако, если вы можете, предпочтительнее использовать классы, которые могут использоваться более широко, а не только для инициализации. Вы можете вместо этого учесть ваши классы по-другому. Я чувствую возможность запаха кода: содержит ли ваш класс TheForm слишком много бизнес-логики? Например, вы хотите разделить контроллер MVC?

1 голос
/ 02 февраля 2010

Пересылка конструктора!

...