Здравствуйте, Gattaka, Вы писали:
S>>А зачем там синхронизация, если нет разделяемого состояния? G>А откуда вы знаете что нет? И тем более компилятор откуда знает?
Как насчёт варианта "потому что блокировки нет в коде"?
Рантайм использует блокировки для static-конструкторов, это да. Для всего остального — только по пожеланиям трудящихся.
Здравствуйте, hardcase, Вы писали:
H>Здравствуйте, Gattaka, Вы писали:
G>>Опять же как тестировать ваш код со статическими методами?
H>Пишется тест, который вызывает статический метод.
Я прекрасно понимаю, что можно выкрутиться. Но зачем добавлять либо еще один метод, либо еще один фейковый тип. Либо синоним как предложил автор, если есть готовый подход. Знакомый любому нормальному программеру. Ваш код будет читаемый, сопровождаемый, простой. Самое главное простой. За счет общеупотребительнсоти IoC.
Здравствуйте, _NN_, Вы писали: _NN>У второго варианта удобнее использование, ведь X,Y будут скрыты от глаз, зато первый вариант не создает ненужного наследования.
Как правило, если у вас возникает вот такой вот "популярный алиас", то недалеко и то время, когда он станет отличаться от от предка.
Поэтому введение алиаса типа NameValueCollection: Dictionary<string, string> оправдано.
Дело не в том, скрыты ли от глаз X и Y. А в том, как вы выражаете требования — если у вас какой-то метод принимает Dictionary<string, string>, то он не вправе ожидать, скажем, case-insensitive сравнения ключей.
Даже если вы сделаете фабрику NameValueCollection с методом Create(), который верно проинициализирует предка, то нет способа потребовать в аргументах именно такой объект.
А как только вы объявили свой NameValueCollection, то тут же получаете возможность явно требовать именно его и полагаться на консистентную инициализацию (а в будущем — и на какие-то дополнительные инварианты, которые вы обеспечите перекрытием унаследованных методов и другим нетривиальным кодом).
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, _NN_, Вы писали:
_NN>В силу отсутствия глобальных псевдонимов типов в C# задался вопросом как лучше сделать.
А я за третий вариант:
class SomeSpecific
{
public static SomeSpecific<X, Y> Create(X x, Y y) {return new SomeSpecific(x, y); }
public static SomeSpecific<X, Y> Create() {return Create(new X(), new Y()); }
}
class SomeSpecific<X, Y> {...}
Этот вариант особенно ок, если есть вполне реальные шансы, что нужно будет создавать объекты с не дефолтными параметрами. В этом случае мы толком и не создаем дополнительных абстракций, а просто используем вполне распространенный трюк для обхода ограничения C#, который не умеет выводить обобщенные параметры при вызове конструктора.
Здравствуйте, Gattaka, Вы писали:
G>>>Зачем это городить если уже есть готовое решение в виде IoC контейнеров разнообразных. Опять же как тестировать ваш код со статическими методами? _NN>>А зачем это тут ? _NN>>Чем плохи статические методы ? G>Тем что вводите еще одну сущность, не используя готовые инструменты. Увеличиваете сложность.
Кто сказал, что тут нужно что-то инджектить и что этим типом нельзя пользоваться? Кто сказал, что эта зависимость изменчивая? Кто сказал, что "готовый инструмент" подходит для этой задачи?
Использование готовых инструментов не по назначению — это главная причина увеличения сложности.
Здравствуйте, Gattaka, Вы писали:
H>>Пишется тест, который вызывает статический метод.
G>Я прекрасно понимаю, что можно выкрутиться. Но зачем добавлять либо еще один метод, либо еще один фейковый тип. Либо синоним как предложил автор, если есть готовый подход. Знакомый любому нормальному программеру. Ваш код будет читаемый, сопровождаемый, простой. Самое главное простой. За счет общеупотребительнсоти IoC.
Скажите, у вас весь код так написан ?
Вместо, скажем, string.Compare(a,b) пишете там ServiceLocator.Get<IStringComparer>(a, b) ?
Здравствуйте, Gattaka, Вы писали:
G>Ну так есть ведь флажок beforefieldinit. Который сказывается на производительности. Т.е. достаточно вам только добавить статический конструктор к типу, как получаете просадку по производительности в неожиданном месте. Плюс есть соблазн работать из статического метода с другими полями, а не только создавать экземпляр. Нужно синхронизировать и т.п. Вобщем решение со статическими методами всегда выглядит не очень здорово. Лично я стараюсь избегать статических методов.
Это очень смелое утверждение. Андрей Акиньшин недавно показал, когда это может произойти, но я не помню как-то кейсов, когда проверка статического конструктора *настолько* влияла на производительность end-2-end приложения, чтобы нужно было менять дизайн, чтобы обойти это дело.
И да, "достаточно" закоммитить Thread.Sleep в 73-х местах и любое приложение будет тормозным. Будет проблема, будет профилирование и будет решение. Сильно много future proofing-а в предыдущем абзаце.
и готов повторить: статические методы — это не хорошо или плохо. Статические методы с побочными эффектами — чистое зло, а чистые статические методы — это просто прекрасно, ибо их проще читать, тестить и реюзать. И ООП embrace-ит это не хуже ФП, пусть и ничего не гарантирует. Если же именно этот факт кого-то пугает (что ведь можно что-то поменять, и все сломать), то как тогда вообще жить? ведь кто-то может получить доступ через рефлексию к типу вашего массива и сменить его тип во время исполнения? Все, что угодно можно использовать неправильно и вопрос лишь в том, насколько это легко сделать? С моей колокольни использовать неправильно контейнер легче, чем использовать неверно статический метод, ибо первое — это один большой стейт и god-object, а второй — это примитив из которого строится функциональность приложения.
Здравствуйте, Gattaka, Вы писали:
G>Продолбал регистрацию — это вопрос опыта работы с IoC. Неопытные программисты поначалу на структурах могут наколоться. А потом — норм.
Это не вопрос опыта. Это вопрос размеров проекта. В большом проекте регистрация теряется легко и непринужденно в каком-нибудь стороннем модуле. И искать ее потом можно очень долго. Особенно если сторонний модуль является черным ящиком, для которого даже сорцов нет.
Happy debugging.
Здравствуйте, Gattaka, Вы писали:
AVK>>Не, поэтому рулит статический ресолвинг. G>Что такое статический резолвинг? Не понял.
Это когда в вижаке нажал F12 на вызов конструктора и попал на определение конструктора. Очень удобно, надо сказать
На IoC свет клином не сошелся, однако.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, _NN_, Вы писали: _NN>>У второго варианта удобнее использование, ведь X,Y будут скрыты от глаз, зато первый вариант не создает ненужного наследования. S>Как правило, если у вас возникает вот такой вот "популярный алиас", то недалеко и то время, когда он станет отличаться от от предка. S>Поэтому введение алиаса типа NameValueCollection: Dictionary<string, string> оправдано. S>Дело не в том, скрыты ли от глаз X и Y. А в том, как вы выражаете требования — если у вас какой-то метод принимает Dictionary<string, string>, то он не вправе ожидать, скажем, case-insensitive сравнения ключей. S>Даже если вы сделаете фабрику NameValueCollection с методом Create(), который верно проинициализирует предка, то нет способа потребовать в аргументах именно такой объект. S>А как только вы объявили свой NameValueCollection, то тут же получаете возможность явно требовать именно его и полагаться на консистентную инициализацию (а в будущем — и на какие-то дополнительные инварианты, которые вы обеспечите перекрытием унаследованных методов и другим нетривиальным кодом).
Основная странность здесь в стремлении скрыть X и Y. Это как бы намекает, что зря сделали дженерик если возникает такое желание скрыть.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, _NN_, Вы писали:
_NN>>В силу отсутствия глобальных псевдонимов типов в C# задался вопросом как лучше сделать.
S>А у TA и TB есть ограничения по базовым типам?
Ограничений нет.
S>Довольно кривое решение, но оно работает в случае, когда надо отнаследоваться от SomeSpecific и подсунуть наследника от X или Y.
От SomeSpecific наследоваться не потребуется.
Здравствуйте, Gattaka, Вы писали:
G>Опять же с точки зрения многопоточности будет lock на статическом методе. В отличии от второго подхода.
Или я ошибаюсь, или ты ошибаешься
Здравствуйте, SergeyT., Вы писали:
ST>Здравствуйте, _NN_, Вы писали:
_NN>>В силу отсутствия глобальных псевдонимов типов в C# задался вопросом как лучше сделать.
ST>А я за третий вариант:
ST>
ST>class SomeSpecific
ST>{
ST> public static SomeSpecific<X, Y> Create(X x, Y y) {return new SomeSpecific(x, y); }
ST> public static SomeSpecific<X, Y> Create() {return Create(new X(), new Y()); }
ST>}
ST>class SomeSpecific<X, Y> {...}
ST>
ST>Этот вариант особенно ок, если есть вполне реальные шансы, что нужно будет создавать объекты с не дефолтными параметрами. В этом случае мы толком и не создаем дополнительных абстракций, а просто используем вполне распространенный трюк для обхода ограничения C#, который не умеет выводить обобщенные параметры при вызове конструктора.
Зачем это городить если уже есть готовое решение в виде IoC контейнеров разнообразных. Опять же как тестировать ваш код со статическими методами?
H>Тем, что надо таскать ServiceLocator — это раз, а также понять, кто и где продолбал регистрацию, если внезапно не срослось.
Продолбал регистрацию — это вопрос опыта работы с IoC. Неопытные программисты поначалу на структурах могут наколоться. А потом — норм.
Предложенный автором подход с синонимом — самое плохое решение. Читаемость кода ухудшается. Да вы экономите символы при написании инициализации. Но возникает ряд вопросов. Когда и в каких случаях использовать синонимы, а когда нет. Где эта грань? Чтобы было понятнее juniorы c помощью этой конструкции будут городить чудеса. Аля new Sometype(5,7) и new Sometype(4,5).
Вводится еще одна сущность, которая приблизит C# к тому кошмару, что творится в С++.
Здравствуйте, NetDeveloper, Вы писали:
ND>Второй вариант проще поддерживать в коде.
Первый вариант в обще странный какой-то. Опять же с точки зрения многопоточности будет lock на статическом методе. В отличии от второго подхода.
Здравствуйте, _NN_, Вы писали:
_NN>В силу отсутствия глобальных псевдонимов типов в C# задался вопросом как лучше сделать.
А у TA и TB есть ограничения по базовым типам?
Если да и TA и TB — не структуры, то я бы сделал не-generic SomeBase, который принимал бы базовые типы, в наследниках добавлял бы свойство, которое явно кастило бы к нужному типу, генерик-параметры вообще бы не использовал.
Довольно кривое решение, но оно работает в случае, когда надо отнаследоваться от SomeSpecific и подсунуть наследника от X или Y.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Gattaka, Вы писали:
G>>Опять же с точки зрения многопоточности будет lock на статическом методе. В отличии от второго подхода. S>Или я ошибаюсь, или ты ошибаешься S>Нету там lock, но это меньшая из проблем
Ну и как синхронизация происходит при обращении к статическому методу из разных потоков?
Здравствуйте, _NN_, Вы писали:
_NN>Здравствуйте, Gattaka, Вы писали:
G>>А чем не угодил вариант:
G>>
G>>b = new SomeGeneric<X, Y>(new X(), new B());
G>>
_NN>Тем, что нужно каждый раз это писать как бы.
Да, но код становится проще для понимания. Не вводится никаких дополнительных сущностей. Ты сразу видишь что происходит. Тебе не нужно лезть в Create или какой-то мутный пустой класс или что хуже синоним, как предложили выше.
Опять же почему был введен дженерик если приходится вводить тип обрезающий дженерик до обычного не дженерик класса? Это оправдано?
G>Да, но код становится проще для понимания. Не вводится никаких дополнительных сущностей. Ты сразу видишь что происходит. Тебе не нужно лезть в Create или какой-то мутный пустой класс или что хуже синоним, как предложили выше.
Но тогда использование не очень..
G>Опять же почему был введен дженерик если приходится вводить тип обрезающий дженерик до обычного не дженерик класса? Это оправдано?
Оправданно.
Там общий интерфейс плана
interface IConvertible<X,Y> { }
Могу предложить аналогию с Dictionary<TKey, TValue>.
Часто я пишу код
using SpecificDictionary = Dictionary<string, string>;
Но это работает в пределах файла и вытащить наружу никак.
Я уже
Здравствуйте, Gattaka, Вы писали:
G>Зачем это городить если уже есть готовое решение в виде IoC контейнеров разнообразных. Опять же как тестировать ваш код со статическими методами?
А зачем это тут ?
Чем плохи статические методы ?
Здравствуйте, _NN_, Вы писали:
_NN>Здравствуйте, Gattaka, Вы писали:
G>>Зачем это городить если уже есть готовое решение в виде IoC контейнеров разнообразных. Опять же как тестировать ваш код со статическими методами? _NN>А зачем это тут ? _NN>Чем плохи статические методы ?
Тем что вводите еще одну сущность, не используя готовые инструменты. Увеличиваете сложность.
Здравствуйте, SergeyT., Вы писали: ST>Кто сказал, что тут нужно что-то инджектить и что этим типом нельзя пользоваться? Кто сказал, что эта зависимость изменчивая? Кто сказал, что "готовый инструмент" подходит для этой задачи?
Да, тут много недосказанностей. Но автор топика не хочет делать просто new Sometype<X,B>(new X(), new B()). А ведь этот код стоит использовать в случае отрицательного ответа на ваши вопросы. В случае пложительного — IoC.
Таким образом Create и введение синонима в любом случае отпадают.
ST>Использование готовых инструментов не по назначению — это главная причина увеличения сложности.
Утверждение не оспариваю. Напротив — согласен. В данной задаче следует по возможности оттягивать использования IoC.
Здравствуйте, _NN_, Вы писали:
_NN>Скажите, у вас весь код так написан ? _NN>Вместо, скажем, string.Compare(a,b) пишете там ServiceLocator.Get<IStringComparer>(a, b) ?
Мы вобще про создание объекта говорили. Зачем передергивать? string.Compare(a,b) — содержит в себе логику. Ничего не создает.
Давайте на чистоту, когда вы задавали вопрос у вас уже был ответ в виде синонима. И вся печаль в том, что синоним неудачное решение, повторение ошибок Си.
Вы пытались найти сторонников. Но на самом деле для C# было бы лучше не добавление синонимов, а реализация нормального встроенного IoC. (не MEF, который ни разу не IoC). Чтобы люди не говорили — "О-о-о, ради такой фитюльки IoC в проект тащить".
Здравствуйте, Lexey, Вы писали:
L>Здравствуйте, Gattaka, Вы писали:
G>>Продолбал регистрацию — это вопрос опыта работы с IoC. Неопытные программисты поначалу на структурах могут наколоться. А потом — норм.
L>Это не вопрос опыта. Это вопрос размеров проекта. В большом проекте регистрация теряется легко и непринужденно в каком-нибудь стороннем модуле. И искать ее потом можно очень долго. Особенно если сторонний модуль является черным ящиком, для которого даже сорцов нет. L>Happy debugging.
Здравствуйте, Gattaka, Вы писали:
G>Здравствуйте, _NN_, Вы писали:
_NN>>Скажите, у вас весь код так написан ? _NN>>Вместо, скажем, string.Compare(a,b) пишете там ServiceLocator.Get<IStringComparer>(a, b) ?
G>Мы вобще про создание объекта говорили. Зачем передергивать? string.Compare(a,b) — содержит в себе логику. Ничего не создает. G>Давайте на чистоту, когда вы задавали вопрос у вас уже был ответ в виде синонима. И вся печаль в том, что синоним неудачное решение, повторение ошибок Си.
Какие аргументы против ?
Т.е. в коде 'using C = System.Console;' тоже плохо ?
G>Вы пытались найти сторонников. Но на самом деле для C# было бы лучше не добавление синонимов, а реализация нормального встроенного IoC. (не MEF, который ни разу не IoC). Чтобы люди не говорили — "О-о-о, ради такой фитюльки IoC в проект тащить".
Пример в студию.
Я думаю нет встроенного потому как каждому нужно что-то своё, а в встроенный не всех будет устраивать.
Здравствуйте, _NN_, Вы писали:
_NN>Какие аргументы против ? _NN>Т.е. в коде 'using C = System.Console;' тоже плохо ?
А что хорошего? Это экономия букв за счет прозрачности и понятности кода. Эту конструкцию следует использовать только при наличии конфликтов, что крайне редко происходит.
G>>Вы пытались найти сторонников. Но на самом деле для C# было бы лучше не добавление синонимов, а реализация нормального встроенного IoC. (не MEF, который ни разу не IoC). Чтобы люди не говорили — "О-о-о, ради такой фитюльки IoC в проект тащить". _NN>Пример в студию. _NN>Я думаю нет встроенного потому как каждому нужно что-то своё, а в встроенный не всех будет устраивать.
Очевидно же — тем кому не нравится будут использовать свое. Это как с Hashset — кому не нравится используют C5. Но подавляющее большенство используют стандартный.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Gattaka, Вы писали: G>>Вот поэтому микросервисы рулят.
AVK>Не, поэтому рулит статический ресолвинг.
Что такое статический резолвинг? Не понял.
Здравствуйте, Sinix, Вы писали:
S>Здравствуйте, Gattaka, Вы писали:
S>>>А зачем там синхронизация, если нет разделяемого состояния? G>>А откуда вы знаете что нет? И тем более компилятор откуда знает? S>Как насчёт варианта "потому что блокировки нет в коде"?
S>Рантайм использует блокировки для static-конструкторов, это да. Для всего остального — только по пожеланиям трудящихся.
А как насчет первого вызова статического метода из кучи потоков? Должен ведь конструктор типа вызваться. В какой момент снимается блокировка на инициализацию типа? Нужно ведь проверить был ли вызов конструктора типа.
Здравствуйте, Gattaka, Вы писали:
G>Здравствуйте, _NN_, Вы писали:
_NN>>Какие аргументы против ? _NN>>Т.е. в коде 'using C = System.Console;' тоже плохо ? G>А что хорошего? Это экономия букв за счет прозрачности и понятности кода. Эту конструкцию следует использовать только при наличии конфликтов, что крайне редко происходит.
Введение алиасов следует использовать лишь когда это себя оправдывает. На практике зачастую ее используют чтобы сократить в контексте файла обобщенные типы от многих аргументов: словари, кортежи, или их произвольные комбинации.
Здравствуйте, Gattaka, Вы писали:
G>А как насчет первого вызова статического метода из кучи потоков? Должен ведь конструктор типа вызваться. В какой момент снимается блокировка на инициализацию типа? Нужно ведь проверить был ли вызов конструктора типа.
Проверять вызов конструктора типа нужно только если метод обращается к статическим данным это раз, и для этого не требуется захватывать блокировку, это два. После вызова конструктора типа рантайм имеет всю информацию для того, чтобы удалить проверку из остальных методов, делает ли он это, мне не ведомо.
Здравствуйте, hardcase, Вы писали:
H>Здравствуйте, Gattaka, Вы писали:
G>>А как насчет первого вызова статического метода из кучи потоков? Должен ведь конструктор типа вызваться. В какой момент снимается блокировка на инициализацию типа? Нужно ведь проверить был ли вызов конструктора типа.
H>Проверять вызов конструктора типа нужно только если метод обращается к статическим данным это раз, и для этого не требуется захватывать блокировку, это два. После вызова конструктора типа рантайм имеет всю информацию для того, чтобы удалить проверку из остальных методов, делает ли он это, мне не ведомо.
Ну так есть ведь флажок beforefieldinit. Который сказывается на производительности. Т.е. достаточно вам только добавить статический конструктор к типу, как получаете просадку по производительности в неожиданном месте. Плюс есть соблазн работать из статического метода с другими полями, а не только создавать экземпляр. Нужно синхронизировать и т.п. Вобщем решение со статическими методами всегда выглядит не очень здорово. Лично я стараюсь избегать статических методов.
Здравствуйте, Gattaka, Вы писали:
G>Основная странность здесь в стремлении скрыть X и Y. Это как бы намекает, что зря сделали дженерик если возникает такое желание скрыть.
Не уверен, что изначальная формулировка верна. Прямо "скрыть скрыть" всё равно не получится — скорее всего, речь о том, чтобы а) убрать с видного места (Кого там интересует, что мы наследуемся от IReadWriteOperations<T>, где T — это CustomerOperationsAbstract<Arithmetic<BigInt>, RetryLogic.ExponentialRetryLogic>>) и б) прекатить обязать всё это выписывать при каждом использовании.
Так-то никаких проблем нету — очень часто "частные специфичные" вещи очень похожи (до неотличимости) на удобный дженерик.
Ну вот из простейшего — целочисленный Random изоморфен IEnumerable<int>.
Но это же не означает, что IEnumerable<int> зря объявлен дженериком.
Тем не менее, если я пишу какой-то код, который может работать с разными генераторами ПСЧ, то лучше его типом параметра всё же взять IRandomSequenceProvider, даже если этот интерфейс ничего не добавляет к базовому для него IEnumerable<int>. Просто из соображений коммуникации — чтобы избежать искушения скормить в мой крипто-модуль формально подходящий параметр вроде {0,}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.