Хочу абстрактный конструктор

Философ Философ
Без этой возможности Activator просто вреден, т.к. нет возможности проверить на этапе компиляции существование нужного конструктора. Т.е. выявить ошибку кодирования можно только на этапе выполнения.
Философ
Философ
22.09.2012 12:28
Как выяснилось, далеко не все понимают зачем это нужно.
Недавно произошёл на эту тему весьма показательный разговор (немного похоже на разговор слепого с глухим, но в принципе пофигу).

Копипаста:
  Скрытый текст
>>Raziel
кое-чего мне до сих пор не хватает.
например способа обязать наследников реализовывать нужный мне конструктор.
к сожалению этого нет ни в одном известном мне языке

>Abaddon

фабрики же есть

>>Raziel

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

>Abaddon

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

>>Raziel

как это может?

>Abaddon

это жесть была бы

>>Raziel

покажи

>Abaddon

ну например, есть класс Base::Base(int i), и в наследниках должен быть этот конструктор. А если наследнику требуется дополнительная информация при создании?

>>Raziel

а как её может предоставить обобщённый код, который будет эти самые экземпляры создавать?

вот, допустим, что у нас есть суперХитровыебанная коллекция (кстати, реальная задача)

существующее решение таково:
коллекция держит "контекст", который она передаёт в фабрику

коллекция соответственно обобщённая по типу элементов данных: TEntity, но держит элементы-презентеры (TPresenter), которые и создаёт фабрика
т.е. контекст общий
никакой дополнительной информации ты туда не запихаешь (разве что на уровне статических полей фабрики — но это изврат)
т.е. вполне можно требовать реализацию определённых фабричных методов (или определённых конструкторов).
и новые типы "конструкторов" для этой задачи бесполезны

>>Raziel

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

>Abaddon

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

>>Raziel

пока не надо.
там ведь, наверное, большой код, а у меня нет пока желания что-то такое изучать

>Abaddon

[код выпилен]

>>Raziel

я пока не понял твоей основной мысли

>Abaddon

мысль в том, что если бы для ISink был зафиксировать конструктор, реализовать нихрена было бы невозможно
пропадает расширяемость системы

>>Raziel

всё можно было бы реализовать.
в данном случае общего конструктора для наследников просто нет, соотвественно и фиксировать его не нужно.

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

>Abaddon

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

>>Raziel

нет, Abaddon, ибо когда ты пишешь класс, то либо предполагаешь, что от него можно будет наследоваться (и пишешь его соответственно), либо даёшь возможность накосячить программерам, которые захотят от него наследоваться.
есть ещё третий вариант: объявить класс как sealed


>Abaddon

ну вот как sealed и объявляй, если хочется зафиксировать

смысл ставить какие-то палки в колеса?
ну напишет кто-то класс с конструктором заданного вида, который будет что-то из синглтона подтягивать — тебе от этого легче будет?

>>Raziel

пофигу, ибо экзепляр этого класса по крайней мере можно будет создать (я про такие вещи как Activator)

>Abaddon

и весь этот гемор ради сраного активатора?
нахуй-нахуй

>Abaddon

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

>>Raziel

я-то пропишу (если не забуду)
а вот посторонний человек, может даже и не знать, что этот конструктор вообще-то необходим

>Abaddon

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

>>Raziel

исключение — это рантайм
а я хочу такие ошибки перенести в компайл-тайм
именно в этом смысл

>Abaddon

ну так активатор и создает объект в рантайме


т.е. выяснилось, что основная мысль, которую люди не понимают выглядит примерно так:

При использовании активатора(и фабрики тоже) можно напороться на отсутствие нужного конструктора (или фабрики), всё что останется — выбросить исключение, но это рантайм, т.е. ошибка будет в рантайме.
А я хочу такие ошибки перенести в компайл-тайм.
AndrewVK
AndrewVK
22.09.2012 12:40
Здравствуйте, Философ, Вы писали:

Ф>При использовании активатора(и фабрики тоже) можно напороться на отсутствие нужного конструктора (или фабрики), всё что останется — выбросить исключение, но это рантайм, т.е. ошибка будет в рантайме.


Что касается активатора, то это рефлекшен, там все по определению в рантайме чекается. А насчет фабрики вообще не понял — в каком месте там исключение в рантайме?
... << RSDN@Home 1.2.0 alpha 5 rev. 65 on Windows 8 6.2.9200.0>>
Философ
Философ
22.09.2012 12:56
Здравствуйте, AndrewVK, Вы писали:

AVK>Здравствуйте, Философ, Вы писали:


Ф>>При использовании активатора(и фабрики тоже) можно напороться на отсутствие нужного конструктора (или фабрики), всё что останется — выбросить исключение, но это рантайм, т.е. ошибка будет в рантайме.


AVK>Что касается активатора, то это рефлекшен, там все по определению в рантайме чекается. А насчет фабрики вообще не понял — в каком месте там исключение в рантайме?


Метод получения нужной фабрики может выглядеть например вот так:


      public IUIFactory GetFactory(Type p_type)
         {
         for (int i = 0; i < m_lstFactories.Count; ++i)
            {
            if (m_lstFactories[i].ObjectType == p_type)
               return m_lstFactories[i];
            }
         throw new NotImplementedException("No factory for the type " + p_type.Name);
         }

т.е. сам бросать исключение, или просто возвращать null.

В последнем случае отсутствие, т.е. в случае null'а, это всё равно исключительная ситуация: не можем мы UI пользователю дать, только сообщение об ошибке показать можем.
AndrewVK
AndrewVK
22.09.2012 08:05
Здравствуйте, Философ, Вы писали:

Ф>Метод получения нужной фабрики может выглядеть например вот так:


Т.е. специально сделать так, чтобы компилятор не мог проверить? Интересный подход.
... << RSDN@Home 1.2.0 alpha 5 rev. 65 on Windows 8 6.2.9200.0>>
Философ
Философ
22.09.2012 09:48
Здравствуйте, AndrewVK, Вы писали:

AVK>Здравствуйте, Философ, Вы писали:


Ф>>Метод получения нужной фабрики может выглядеть например вот так:


AVK>Т.е. специально сделать так, чтобы компилятор не мог проверить?


А есть другие варианты?
AndrewVK
AndrewVK
22.09.2012 12:45
Здравствуйте, Философ, Вы писали:

AVK>>Т.е. специально сделать так, чтобы компилятор не мог проверить?


Ф>А есть другие варианты?


Конечно. Про абстрактную фабрику слышал?
... << RSDN@Home 1.2.0 alpha 5 rev. 65 on Windows 8 6.2.9200.0>>
Философ
Философ
23.09.2012 07:13
Здравствуйте, AndrewVK, Вы писали:

AVK>Здравствуйте, Философ, Вы писали:


AVK>>>Т.е. специально сделать так, чтобы компилятор не мог проверить?


Ф>>А есть другие варианты?


AVK>Конечно. Про абстрактную фабрику слышал?


я именно её и привёл.
IUIFactory это и есть абстрактная фабрика.

Если знаешь другой способ, то напишите его здесь — может я заблуждаюсь где-то.
Sinclair
Sinclair
15.11.2012 06:42
Здравствуйте, Философ, Вы писали:

Ф>А есть другие варианты?

Самый простой вариант в дотнете — вместо "фабрики" использовать делегат.
Вот у вас, допустим, класс "коллекция", который хочет создавать новые элементы определённым образом:
public class NamedArray<T>
  where T: new(string); // вот было бы охрененно, но не работает
{
   public string Name{get;set;}
...
   public int Count { 
     get { return _count;} 
     set { 
       var count = _count;
       Resize(value);
       while(count < value)
         this[count++] = new T(Name); // сложность тут
     }
}

В приведённом виде код не работает. Но его легко починить вот так:
public class NamedArray<T>
{
   public NamedArray(Func<string, T> factory)
   {
     _factory = factory; // вот оно!
   }

...
   public int Count { 
     get { return _count;} 
     set { 
       var count = _count;
       Resize(value);
       while(count < value)
         this[count++] = _factory(Name); 
     }
}

Всё. Зачем вам все эти сложности с поиском фабрики по типу?
Просто конструируете NamedArray вот так:
var a = new NamedArray<City>((name)=>new City(name));