Какой самый элегантный способ перегрузить конструктор / метод? - 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 ]

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

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

Стоит посмотретьв хорошо разработанном API, который использует перегрузку, чтобы понять, для какой работы подходит этот инструмент.XmlReader.Create является хорошим примером: он поддерживает двенадцать различных перегрузок.Двенадцать!И все же, это на самом деле совершенно разумно: когда вы смотрите на них все, они сводятся к тому, что в Python будет единственной вызывающей сигнатурой с необязательными параметрами:

XmlReader.Create(input [, settings [, parser_context]])

input для этого метода, может быть строкой, содержащей URL или имя файла, TextReader или Stream.Но независимо от типа данных, это по сути одно и то же: источник данных, которые XmlReader будет читать.

Теперь давайте рассмотрим ваш случай.Забудьте о типах данных на мгновение.Очевидно, что в вашем приложении есть некоторая функциональная разница между status и idCode.Ваша форма будет вести себя в одном направлении, если ей дано status, а в другом - если ей дано idCode.API, который вы предлагаете , скрывает эту функциональную разницу.Это должно быть с подсветкой it.

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

TheForm(string idCode, string status)

Заставьте ваш конструктор вызвать исключение, еслиоба значения предоставляются (или если оба являются нулевыми).Обратите внимание, что они взаимоисключающие в документации.Назовите это днем.

Мой второй выбор будет следующим:

enum FormType
{
   IdCode,
   Status
};

TheForm(FormType type, string data)

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

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

class TheFormWhatUsesIdCode : TheForm {...}
class TheFormWhatUsesStatus : TheForm {...}

Функциональная разница между idCode и status, вероятно, связана с функциональной разницей между формой, созданной с помощью idCode, и формой, созданной с помощью status.И это настоятельно предполагает, что они должны быть подклассами.

Во всем этом анализе я ни разу не рассматривал возможность сделать то, что вы на самом деле просили, то есть обеспечить несколько перегрузок.Я не думаю, что перегрузка является правильным инструментом для этой работы.Если бы idCode были int, а status были string I все еще , я бы не подумал, что перегрузка была подходящим инструментом для этой работы, хотя я, вероятно, не заметил быдо тех пор, пока у меня не будет много кода, необходимого для рефакторинга.

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

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

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

В одном месте у нас есть класс, подобный следующему:

using System;

namespace MyApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            base1 t1 = new type1();
            class1 c1 = new class1(t1);
            base1 t2 = new type2();
            class1 c2 = new class1(t2);
            //.....
        }
    }

    public class class1
    {
        public class1(base1 mytype)
        {
           switch(mytype.type)
               case mytype.types.type1
                   return createObjectOftype1();
               case mytype.types.type2
                   return createObjectOftype2();
               case mytype.types.type3
                   return createObjectOftype3();
        }

        public class1 createObjectOftype1()
        {
            //....
        }

        public class1 createObjectOftype2()
        {
           //...
        }

        public class1 createObjectOftype2()
        {
           //...
        }

    }


    public class base1
    {
        publlic Enum Types {0 "type1",.... 
    }

    public class type1:base1
    {
        //.....
    }

    public class type2:base1
    {
        //.....
    }
}
0 голосов
/ 02 февраля 2010

Разве это не то, где приходит наследство? Просто используйте TheForm в качестве базового класса, а затем дочерние классы TheFormWithID и TheFormWithStatus. Пусть их конструкторы принимают идентификатор строки и строку состояния соответственно, передавая значение DateTime базовому классу.

У меня нет никаких инструментов кодирования, поэтому прошу прощения за синтаксис. Я уверен, что вы поймете это.

using System;

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

    public class TheForm
    {
        public TheForm(DateTime startTime)
        {
           //...
        }

    }

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

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

Или используйте TheForm в качестве абстрактного класса.

...