Extensions с generic'ами не живут, что в общем, понятно, но обидно:
static class ListExt<T>
{
public static string First(this List<T> list)
{
return list[0];
}
public static string Last(this List<T> list)
{
return list[list.Count-1];
}
}
...
var list = new List<string> {"aaa", "bbb", "ccc"};
Console.WriteLine(list.First()); // А вот тут хочется extension-свойств!
Не пашет такая попыточка. Хотя вообще-то принципиально это можно было бы сделать.
Всего лишь превращать list.First() не в ListExt<T>.First(list), а в ListExt<string>.First(list), гладя на совпадающее количество type-args.
static class ListExt
{
public static T First<T>(this List<T> list)
{
return list[0];
}
public static T Last<T>(this List<T> list)
{
return list[list.Count-1];
}
}
Параметризировать надо конкретный метод а не класс.
(В предыдущем сообщении у меня вообще string затесался откуда-то )
До сих пор не хватает "красивой" работы с нулевыми значениями
сейчас вместо:
if (Root.Config.User.Age > 16)
{
//bla-bla
}
часто приходиться писать:
int? age = null;
var root = Root;
if (root != null)
{
var config = root.Config;
if (config != null)
{
var user = config.User;
if (user != null)
age = user.Age;
}
}
if (age != null && age.Value > 10)
{
//bla-bla
}
т.е. хочеться иметь оператор вида '.?', который возвращает нуль, если левая часть имеет нулевое значение
if (Root.?Config.?User.?Age > 16)
{
//bla-bla
}
или может даже лучше:
if (Root?.Config?.User?.Age > 16)
{
//bla-bla
}
Особенно это нужно при описании правил бизнес логики, т.к. в бизнес-логики часто мы имеем на руках только частичную информацию о мире
Здравствуйте, DarkGray, Вы писали:
DG>До сих пор не хватает "красивой" работы с нулевыми значениями
DG>т.е. хочеться иметь оператор вида '.?', который возвращает нуль, если левая часть имеет нулевое значение
DG>
Первое всё-таки лучше.
DG>Особенно это нужно при описании правил бизнес логики, т.к. в бизнес-логики часто мы имеем на руках только частичную информацию о мире
Пусть Root == null. Ты предлагаешь, чтобы ((RootClass)null).?Config тоже вычислялось в null? А во что же должно вычисляться все выражение Root.?Config.?User.?Age. В 0 (ноль)? И тип у него естественно будет int?
Дык, возраст со знаением ноль и возраст с неизвестным значением — это ведь две разные вещи.
А если тип будет, например, строка? возвращать пустую строку? А если структура?
Сколько народу ругается на Oracle, где пустая строка и NULL — это одно и тоже!
ИМХО, нужно вводить специальные типы, по анаголии с NULL концепцией в SQL. Так чтобы переменная этого типа могла кроме определенного знаения хранить еще и признак наличия значения, т.е. попросту говоря могла бы не иметь значения. И соответственно вводить специальные операции с такими типами.
Я в своем DSL, основа которого была нагло стырена с C#, ввел такие типы. null оставил как есть в C#, а в дополнение к этому ввел константу undefined (типа <undefined>, как и null в C# имеет специальный тип <null>).
int x = 0; // Обыный int.int% y = 0; // Как я его назвал - undefinable int.if (defined y) // Пройдет. Аналогия с SQL-ым IS NULL.
...;
y = undefined;
if (y == undefined) // Не пройдет. Так как undefined != undefined, как в SQL.
...;
x = (int)y; // Требуется явное приведение. В ран-тайм будет ошибка.
y = y + x; // Выполнится как y + (int%)x. Получится undefined.
Это для простых типов. Теперь про классы с полями и свойствами:
class A
{
public B% b;
}
class B
{
public int x;
}
A% a = new A();
a.b = undefined;
int% y = a.b.x; // y будет undefined. Так как a.b - undefined.
// Несмотря на то, что поле x имеет тип int,
// операция ((B%)somevar).x имеет тип int%.
// но только на чтение.
a.b.x = 7; // В ран-тайм будет ошибка. На запись a.b.x
// имеет тип int.
Также работают свойства и индексеры.
Дык вот о птичках. Вроде в C# уже ввели работу с SQL-like типами. Там вроде какой-то стандартный дженерик есть, не помню как он там называется. С помощью такого дженерика и перегрузки операторов конечно можно имитировать работу с примитивными SQL-like типами.
А вот с полями и свойствами так как я показал — фигушки...
Если бы был мощный препроцессор, можно быть бы написать универсальный макрос, принимающий конструкцию `a.b.c.*.z`, проверяющий каждый член на null и возвращающий некое значение по умолчанию в таком случае. Но вот этого в C# что-то не предвидится.
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, DarkGray, Вы писали:
DG>>т.е. хочеться иметь оператор вида '.?', который возвращает нуль, если левая часть имеет нулевое значение
S>Интересная идея.
DG>>
S>Пусть Root == null. Ты предлагаешь, чтобы ((RootClass)null).?Config тоже вычислялось в null? А во что же должно вычисляться все выражение Root.?Config.?User.?Age. В 0 (ноль)? И тип у него естественно будет int? S>Дык, возраст со знаением ноль и возраст с неизвестным значением — это ведь две разные вещи. S>А если тип будет, например, строка? возвращать пустую строку? А если структура?
S>Сколько народу ругается на Oracle, где пустая строка и NULL — это одно и тоже!
Я думаю, необязательно именно возвращать NULL. Можно, например, просто выходить без исключения из условного оператора, так как условие заранее не выполнено.
FDS>Я думаю, необязательно именно возвращать NULL. Можно, например, просто выходить без исключения из условного оператора, так как условие заранее не выполнено.
Где не обязательно возвращать? Какой порядок выисления выражений то?
S>>Какого типа каждое из этих промежуточных выражений? FDS>CObject
Ну-ну. А разве CObject имеет поле Config? А? Или может быть поле User. Или может быть CObject можно сравнивать с int (c 16)? Чушь!
FDS>Root.Config инициализированно? — нет — выйти из условного оператора
FDS>Root.?Config.?User инициализированно? — аналогично
if (!(Root.?Config.?User.?Age > 16))
...;
if ((Root.?Config.?User.?Age > 16) || someBoolVar)
...;
bool x = (Root.?Config.?User.?Age > 16);
Здравствуйте, FDSC, Вы писали:
FDS>Компилятор должен обрабатывать примерно так: FDS>Root.Config инициализированно? — нет — выйти из условного оператора FDS>Root.?Config.?User инициализированно? — аналогично
Людям явно не нравится механизм обработки исключений и они ощут альтернативы
Здравствуйте, Кодёнок, Вы писали:
Кё>Людям явно не нравится механизм обработки исключений и они ощут альтернативы
А при чем здесь исключения. null есть только для ссылочных типов. А, например, возмем структуру Address. Пусть адрес может быть не задан по условию задачи. И что? Придется заводить еще и булевый признак? Не зря же в SQL сделали NULL. Да и не только в SQL.
Здравствуйте, stalcer, Вы писали:
S>Ну-ну. А разве CObject имеет поле Config? А? Или может быть поле User. Или может быть CObject можно сравнивать с int (c 16)? Чушь!
FDS>>Root.Config инициализированно? — нет — выйти из условного оператора
FDS>>Root.?Config.?User инициализированно? — аналогично
S>
Здравствуйте, FDSC, Вы писали:
FDS>Здравствуйте, stalcer, Вы писали:
S>>Ну-ну. А разве CObject имеет поле Config? А? Или может быть поле User. Или может быть CObject можно сравнивать с int (c 16)? Чушь!
Я имею ввиду, что для компилятора проверяемое поле — указатель на базовый тип
Здравствуйте, FDSC, Вы писали:
FDS>Повторяю, из условного оператора "if"
А вообще интересная идея — считать условие невыполненным, если оно не может быть вычислено (а не тупо сравнивать числа).
Если нас это устраивает, мы просто пишем
if (a.b > 10)
Если надо наоборот — чтобы оно выполнилось в случае невычислимости, то пишем
if (!(a.b <= 10))
Проблема в том, что будут ситуации, когда некоторые случаи невычислиости должны обязательно выкидывать исключение. Т.е. если a.b === null — исключение, а если a.b.c === null — считать условие невыполненным. И еще не ко всякому условию можно записать обратное через !(...)
Здравствуйте, FDSC, Вы писали:
FDS>Я имею ввиду, что для компилятора проверяемое поле — указатель на базовый тип
Ты ответь на мой вопрос. Какого типа, с точки зрения предполагаемой спецификации языка должно быть каждое из перечисленных мной выражений. И какое значение каждое из них должно вернуть. Особенно <...>.?Age.
С точки зрения логики. Ведь именно с этой точки зрения мы обсуждаем введение нового типа выражения ".?" в язык.
Его синтаксис можно обозначить так:
<primary> ".?" <field-name>
Где <primary> — это тоже выражение. Вот и объясни мне правила выисления этого выражения вне зависимости от контекста (наличия if, ect.). Объясни также как это объяснено для других выражений в спецификации.
Здравствуйте, Кодёнок, Вы писали:
Кё>Проблема в том, что будут ситуации, когда некоторые случаи невычислиости должны обязательно выкидывать исключение. Т.е. если a.b === null — исключение, а если a.b.c === null — считать условие невыполненным. И еще не ко всякому условию можно записать обратное через !(...)
Тогда можно записать так a.b.?c вместо a.?b.?c. Если b == NULL — в первом случае будет поднято исключение
FDS>>Последние два примера не корректны для данной постановки задачи.
S>Почему это, интересно знать?
Согласен. Всякий синтаксически правильный код — корректен.
S>Пучть Root = null, а someBoolVar = true. S>Второй пример: должно получиться if (false || true), т.е. if должен выполниться. S>Третий пример: x = false;
S>Все корректно. И вообще, любое выражение я могу применять в любом контексте. Из любых более мелких выражений я могу конструировать более сложные.
Кодёнок предложил считать условие не выполненным, если соотв. значение некорректно.
Тогда всё получается.
Здравствуйте, stalcer, Вы писали:
S>Его синтаксис можно обозначить так:
S><primary> ".?" <field-name>
S>Где <primary> — это тоже выражение. Вот и объясни мне правила выисления этого выражения вне зависимости от контекста (наличия if, ect.). Объясни также как это объяснено для других выражений в спецификации.
Каким должен быть <EpressionType>? С одной стороны — типом поля Object2. С другой стороны по твоей логике — bool (так как ты ему false присваиваешь). Как я и говорил — чушь.
Здравствуйте, stalcer, Вы писали:
S>Неа. Вот это: S>
S>bool x = (Root.?Config.?User.?Age > 16);
S>
S>Я перепишу так:
S>
S>int age = Root.?Config.?User.?Age;
S>bool x = (age > 16);
S>
S>Утверждаю, что это должно быть абсолютно эквивалентно первому варианту. Об этом даже спорить не буду.
Согласен...
S>Вопрос 1: правильно ли я объявил переменную age типа int. Или должен быть другой тип? S>Вопрос 2: чему будет равно значение переменной age?
Честно говоря, не знаю, как это правильно объявить. Но оператор .? должен возвращать булевский тип. Вообще-то ерудна получается.
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, FDSC, Вы писали:
FDS>>В общем, примерно так:
FDS>><object1>.?<Object2> == if (Object1.Object != NULL) ... ; else return false;
S>А вот и фигушки. Для начала расшифрую твое многоточие. Ты, наверное, имел ввиду следующее:
S>if (Object1.Object2 != NULL) return Object1.Object2; else return false;
S>А вот теперь подумай:
S>
S>Каким должен быть <EpressionType>? С одной стороны — типом поля Object2. С другой стороны по твоей логике — bool (так как ты ему false присваиваешь). Как я и говорил — чушь.
Правильно.
Надо ввести тогда оператор .? ... .?... условие . Тогда оператор станет возвращать bool в обоих случаях. Только как это сделать?
Здравствуйте, FDSC, Вы писали:
FDS>Честно говоря, не знаю, как это правильно объявить. Но оператор .? должен возвращать булевский тип. Вообще-то ерудна получается.
FDS>Надо долго думать...
Нет, оператор не дожен возвращать никакой тип, иначе ничего не выйдет Именно это мне и показалось интересным — не простое вычисление выражения в операторе if по правилам языка, а какое-то более сложное поведение, в данном случае — считать само условие невыполненным в случае невозможности вычислить аргументы.
Т.е. этот код
if (Object1.?Object2.?Object3)
DoSmth();
должен транслироваться в
if (Object1 != null && Object1.Object2 != null)
{
if (Object1.Object2.Object3)
DoSmth();
}
Т.е. .? не просто оператор, который ведет себя каким-то хитрым образом, а изменение поведения компилятора.
Проблема тут сразу очевидна — как отличить "провал" оператора .? от нормального выполнения? Что если провал одного из операторов должен приводить к невыполнению всего условия, а провал другого — к выполнению всего условия? Еще проблема — если этот оператор влияет на способ всего вычисления выражения в if, то могут быть какие-нибудь неочевидные проблемы.
Кё>Т.е. .? не просто оператор, который ведет себя каким-то хитрым образом, а изменение поведения компилятора.
Кё>Проблема тут сразу очевидна — как отличить "провал" оператора .? от нормального выполнения? Что если провал одного из операторов должен приводить к невыполнению всего условия, а провал другого — к выполнению всего условия? Еще проблема — если этот оператор влияет на способ всего вычисления выражения в if, то могут быть какие-нибудь неочевидные проблемы.
Я собственно то же это хотел. Но возникает вопрос синтаксиса при использовании оператора .? вне оператора if.
Можно ввести разновидность оператора .? и .~?, которые будут всё-таки возвращать булевский тип для определённого условия, но работать только в команде с условием.
Здравствуйте, Кодёнок, Вы писали:
Кё>...то могут быть какие-нибудь неочевидные проблемы.
Очевидная проблема, в том, то невозможно формально описать логику поведения такого оператора.
Возможно только если он выполняется обычным образом, и в нашем примере (с Age) возвращает 0 (ноль) типа int.
То есть пусть Object1.?Age выполняется так:
if (Object1 != null)
return Object1.Age;
else
return (AgeType)default_value;
Где, AgeType — тип поля Age, а default_value — значение, зависящее от AgeType. Для ссылочных типов default_value = null, для числовых — 0, для строки — "", для структур — ??? может структура забитая нулями.
Это, ИМХО, единственный нормальный способ описать поведение такого оператора. Только не очень красиво, Age может быть и так равен нулю.
Или же расширить синтаксис до такого, например: Object1.?Age{7}, что будет означать, то вместо default_value использовать 7.
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, Кодёнок, Вы писали:
Кё>>...то могут быть какие-нибудь неочевидные проблемы.
S>Очевидная проблема, в том, то невозможно формально описать логику поведения такого оператора. S>Возможно только если он выполняется обычным образом, и в нашем примере (с Age) возвращает 0 (ноль) типа int.
S>То есть пусть Object1.?Age выполняется так: S>
S>Где, AgeType — тип поля Age, а default_value — значение, зависящее от AgeType. Для ссылочных типов default_value = null, для числовых — 0, для строки — "", для структур — ??? может структура забитая нулями.
S>Это, ИМХО, единственный нормальный способ описать поведение такого оператора. Только не очень красиво, Age может быть и так равен нулю.
S>Или же расширить синтаксис до такого, например: Object1.?Age{7}, что будет означать, то вместо default_value использовать 7.
Не пойдёт! К сожалению. Смысл в том, что оператор должен прекращать выполнение обработки условного выражения, а не возвращать значение по умолчанию. Как тогда справиться с последовательным вызовом a.?b.?c.? Хранить "нулевые" объекты-значения по умолчанию?
Здравствуйте, FDSC, Вы писали:
FDS>Не пойдёт! К сожалению. Смысл в том, что оператор должен прекращать выполнение обработки условного выражения, а не возвращать значение по умолчанию. Как тогда справиться с последовательным вызовом a.?b.?c.? Хранить "нулевые" объекты-значения по умолчанию?
А чего не пройдет-то? С последовательным вызовом как раз все нормально. Например, a.?b.?c.?Age
Пусть поле 'b' будет равно null.
1) A tmp1 = a; // not null
2) B tmp2 = (tmp1 != null) ? tmp1.b : null; // a.b (== null)
3) C tmp3 = (tmp2 != null) ? tmp2.c : null; // null, так как tmp2 == null
4) int tmp4 = (tmp3 != null) ? tmp3.Age : 0; // 0, так как tmp3 == null
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, FDSC, Вы писали:
FDS>>Не пойдёт! К сожалению. Смысл в том, что оператор должен прекращать выполнение обработки условного выражения, а не возвращать значение по умолчанию. Как тогда справиться с последовательным вызовом a.?b.?c.? Хранить "нулевые" объекты-значения по умолчанию?
S>А чего не пройдет-то? С последовательным вызовом как раз все нормально. Например, a.?b.?c.?Age S>Пусть поле 'b' будет равно null.
S>1) A tmp1 = a; // not null S>2) B tmp2 = (tmp1 != null) ? tmp1.b : null; // a.b (== null) S>3) C tmp3 = (tmp2 != null) ? tmp2.c : null; // null, так как tmp2 == null S>4) int tmp4 = (tmp3 != null) ? tmp3.Age : 0; // 0, так как tmp3 == null
S>Результат будет 0 (ноль).
Здравствуйте, FDSC, Вы писали:
FDS>Я думаю, необязательно именно возвращать NULL. Можно, например, просто выходить без исключения из условного оператора, так как условие заранее не выполнено.
int age;
try { age = Root.Config.User.Age;}
catch (NullReferenceException) {age=0;}
if (age > 16)
{
bla-bla
|
Значительно короче. И не растет с удлиннением выражения.
... << RSDN@Home 1.1.4 stable rev. 510>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, stalcer, Вы писали:
S>Здравствуйте, FDSC, Вы писали:
S>>>1) A tmp1 = a; // not null S>>>2) B tmp2 = (tmp1 != null) ? tmp1.b : null; // a.b (== null) S>>>3) C tmp3 = (tmp2 != null) ? tmp2.c : null; // null, так как tmp2 == null S>>>4) int tmp4 = (tmp3 != null) ? tmp3.Age : 0; // 0, так как tmp3 == null
S>>>Результат будет 0 (ноль).
FDS>>Ага. Получится NULL > 16?
S>Стоп. tmp4 имеет тип int, и значение не null, а 0.
М-м. Видать я не совсем понял. Надо бы более строгое описание.
Здравствуйте, Sinclair, Вы писали:
S>Здравствуйте, FDSC, Вы писали:
FDS>>Я думаю, необязательно именно возвращать NULL. Можно, например, просто выходить без исключения из условного оператора, так как условие заранее не выполнено. S>
S>Значительно короче. И не растет с удлиннением выражения.
А исполняется это значительно медленее и не очень читаемо (такое впечатление). Тут вопрос был ещё и в том, что возможно один из объектов не может быть равен NULL и это нужно обработать как ошибку.
Здравствуйте, stalcer, Вы писали:
S>То есть оператор '.?' уже есть?
Такого оператора нет. Есть int? те инт который может быть null.
WH>>Те в случае если a == null то любые сравнения с числом возвращают false. S>Операции как в SQL? Наверное не только к сравнениям относится, на и к другим операциям (+, — и т.п.) S>А спецификация есть в инете? Где почитать?
Ищи спецификацию C#2.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Вообще мне не очень понятно (и не очень нравится) это стремление добавить в шарп возможности (и спец-синтаксис)функционального программирования. Так ли оно надо именно в рамках шарпа, если есть F# и т.п? Мне кажется, что эта попытка собрать все в одну кучу-малу до добра не доведет — пропадает простота, лаконичность, концептуальная цельность.. Или это во мне говорит закоренелый консерватор, воспитанный на императивных языках?
Здравствуйте, FDSC, Вы писали:
FDS>А исполняется это значительно медленее
Ну, насчет "значительнее" — зависит только от вероятности того, что кто-то все-таки null. Если такая ситуация встречается слишком часто, то надо будет пересмотреть логику. Тем не менее, premature optimization is the root of all evil. FDS>и не очень читаемо (такое впечатление).
Это ошибочное впечатление. Более короткий код читается всегда легче. Во-первых, здесь с первого взгляда виден весь путь доступа (Root.Config.User.Age) без необходимости продираться через цепочку присваиваний (именно за такой эффект всегда критиковали конструкцию with в паскале). Во-вторых, единство политики обработки отсутствия данных выражено ровно в одной строке — сразу видно, что где бы ни произошел NullReferenceException, мы примем дефолтный возраст за Null. FDS> Тут вопрос был ещё и в том, что возможно один из объектов не может быть равен NULL и это нужно обработать как ошибку.
Простите, но в начальном коде этого не было. См. Re: С# 3.0
Честно говоря, я пришел к выводу, что все нововведения построены ради прозрачного обслуживания DLinq, XLinq. Причем так, чтобы их интеграция небыла заметна для прикладника. Чтобы не было заметно, с чем он работает. Синтаксиса языка явно не хватало потому как для генерации и оптимизации предикатов нужно иметь AST. И оно должно быть в функциональном стиле(без лишнего состояния). Плюс генерация типов (чтобы прозрачно можно было запросы менять). Плюс расширение типов(чтобы добавлять поведение для типов к которому доступа нет, так как он создан тем-же DLinq. Ну таким образом, можно все и приплюсовать.
Здравствуйте, Кодёнок, Вы писали:
Кё>Нет, оператор не дожен возвращать никакой тип, иначе ничего не выйдет Именно это мне и показалось интересным — не простое вычисление выражения в операторе if по правилам языка, а какое-то более сложное поведение, в данном случае — считать само условие невыполненным в случае невозможности вычислить аргументы.
nil возвращаемый из методов в Objective-C /.../ в ответ на посылаемое ему сообщение nil не бросает исключение, а попросту игнорирует это сообщение. То есть, nil из Objective-C ведёт себя как "черная дыра". Это пустота. Это ничто. При посылке к nil-у любого сообщения, всё что вы получите назад — это nil. Не будет никаких исключений, никаких возвращенных значений. Ничего.
/.../
поведение ``поедающее'' сообщения позволяет создавать более элегантный код!
/.../
Предположим, что мы хотим найти последний телефонный номер, который вы набрали со своего офисного телефона, и сохранить этот номер в переменной, скажем, 'последнийНомер'. Далее предположим, что мы хотим сохранить этот номер как строку и затем отобразить его в виджете (или же использовать этот номер позже). Если вы -- это 'человек' в последовательности сообщений приведённой далее, то достаточно ли этой последовательности для получения работоспособной программы?
последнийНомер := человек офис телефон последнийИсходящийНомер какСтрока.
виджет строковоеЗначение: последнийНомер.
Возможно.
Но что, если у человека нет офиса? Или в офисе нет телефона? Или что если это новый телефон и по нему еще никто не звонил? В любом из этих случаев, при использовании ``исключенческого'' nil-а будет выброшено исключение, которое приведёт к остановке программы, если только не будет создан соответствующий обработчик исключения.
А что с nil-ом ``поедающим'' сообщения? В этом случае 'последнийНомер' может в качестве значения принять nil, но всё будет работать и в этом случае. Даже передача nil-а как аргумента для метода #строковоеЗначение:1будет работать, потому что ``поедающий'' nil может нормально работать с виджетами. Совершенно не важно, что аргумент это nil. Всё будет работать без бросания исключений и без заметных с первого взгляда побочных эффектов (на этом подробнее остановимся позже).
Для сравнения, ниже приведён код для работы с ``исключенческим'' nil-ом:
Здравствуйте, FDSC, Вы писали:
FDS>Первое всё-таки лучше.
DG>>Особенно это нужно при описании правил бизнес логики, т.к. в бизнес-логики часто мы имеем на руках только частичную информацию о мире
FDS>То же с этим очень мучаюсь. Просто жуть!
А слабо придумать хорошие значения по умолчанию? Ну, 0 для возраста...
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, FDSC, Вы писали:
FDS>Компилятор должен обрабатывать примерно так:
FDS>Root.Config инициализированно? — нет — выйти из условного оператора
FDS>Root.?Config.?User инициализированно? — аналогично
Создайте для всех этих классов null-объектв которые будут возвращать дефолтную информацию и не мучайтесь. Компилятор тут не причем.
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Для вашей проблемы сто лет назад был придуман паттерн NuulObject. То есть объект выполняющий роль того самого null. При этом он предоставлеяет некие значения которые считаются недействительными. Для ссылочных объектов это специальные синглтон-экземлпяры, для вэлью-типов предопределенные значения. Вот пример использования данного паттерна:
class A
{
public A(int i) { I = i; }
public int I;
public static readonly A Default = new A(-1);
}
class B
{
public B() { A = A.Default; }
public B(A a) { A = a; }
public A A;
public static readonly B Default = new B();
}
class Program
{
static void Main()
{
B b = new B();
int i = b.A.I;
}
}
При этом код классов увеличевается незначительно, а основной код выглядит полностью читабельным и незамусоренным.
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Igor Trofimov, Вы писали:
iT>Вообще мне не очень понятно (и не очень нравится) это стремление добавить в шарп возможности (и спец-синтаксис)функционального программирования. Так ли оно надо именно в рамках шарпа, если есть F# и т.п? Мне кажется, что эта попытка собрать все в одну кучу-малу до добра не доведет — пропадает простота, лаконичность, концептуальная цельность.. Или это во мне говорит закоренелый консерватор, воспитанный на императивных языках?
Функцональные возможности в Шарпе были изначально (делегатов уже достаточно). Просто их было очень неудобно использовать. Во втором шарпе их улучшели, но не самым лучшим образом. В третьем шарпе просто довели до ума идеи второго шарпа. Ничего нового в нем не введено. Просто более краткий синтаксис.
Что же необходимости фунционального стиля... С появлением дженериков делегаты стали самым удобным средством абстрагирования дженерик кода от подробностей конкретных типов. И чем проще можно будет работать с ними тем проще и читабельнее будет код.
К тому же функциональный подход позволяет производить более детальную декомпозицию кода. Так что лишним он не будет.
Ну, и главное, что функциональный стиль не навязывается обязательным. Те кто его не понимает могут пользоваться только его плодами. Не думаю, что это будет проблемой.
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Функцональные возможности в Шарпе были изначально (делегатов уже достаточно). Просто их было очень неудобно использовать. Во втором шарпе их улучшели, но не самым лучшим образом. В третьем шарпе просто довели до ума идеи второго шарпа. Ничего нового в нем не введено. Просто более краткий синтаксис.
Врядли синтаксис тех лямб пойдёт в серию. Там же ни одной скобки нету. Ни круглой ни фигурной. Массы такого абстракционизма не поймут
Здравствуйте, VladD2, Вы писали:
VD>Ну, и главное, что функциональный стиль не навязывается обязательным. Те кто его не понимает могут пользоваться только его плодами. Не думаю, что это будет проблемой.
По-моему, вся фигня крутится вокруг коллекций. Анонимные делегаты пользуются только в коллекциях. В реале, я ни разу не делал анонимный предикат за пределами коллекций(и не видел его использования). Это некоторый флаг Microsoft, чем удобнее пользовать коллекции, тем лучше С# . Очень сомневаюсь что подобный синтаксис выйдет за пределы этого. IMHO Функциональный стиль и объекты одновременно не очень хорошо живут. Объекты с их состоянием и строгой типизированностью — ограничивают функциональный стиль. И ввод неименованных типов, по моему, как козе баян, а может и еще хуже.
Причина вполне понятна. Microsoft делает язык для серверов приложений. И коллекции немаловажная часть DSL. Если еще при этом, прозрачно связать с персистентностью объектов, то такая реализация DSL будет весьма здоровская. Похоже это и есть цель C# 3.0. За пределами коллекций, => по моему не катит.
Здравствуйте, VladD2, Вы писали:
ANS>>Врядли синтаксис тех лямб пойдёт в серию. Там же ни одной скобки нету. Ни круглой ни фигурной. Массы такого абстракционизма не поймут VD>Наоботор пойдут на ура. К стати, скобки там можно приделать. Более того если у лямбды два и более параметра, то без них не обойтись: VD>народ поймет, что это просто такая сокращенная форма записи. VD>А когда он сравнит:
Занятно, что "длина" синтаксиса таки имеет значение
VD>
запись вверху неконсистентна, выбивается из общей массы. Я вроде как за краткость, но нужно же придерживаться некого стиля. Не нравится декларация типов (вот уж от тебя не ожидал ) — убери:
Здравствуйте, GlebZ, Вы писали:
GZ>По-моему, вся фигня крутится вокруг коллекций. Анонимные делегаты пользуются только в коллекциях. В реале, я ни разу не делал анонимный предикат за пределами коллекций(и не видел его использования).
Дык коллекции, точнее списки — это самый часто используемый механизм в программах. И как раз функциональный стиль (ФС) при обработке списков показывает себя очень и очень хоношо.
Причем ты даже не подозревашь насколько часто могут применяться списки. Дело в том, что большинство структур данных и даже алгоритмов можно представить в виде списков. Даже БД и те можно выразить списками. Так что итераторы и функции высшего порядка добавленные в коллекции во втором фрэймворке — это очень вреное решение.
Но на списках приемущества функционального подхода (ФП) не заканчиваются. Функции высшего порядка позволяеют производить декомпозицию кода там, где средствами ООП это или вообще нвозможно сделать, или можно, но уж очень много кода получается.
GZ> Это некоторый флаг Microsoft, чем удобнее пользовать коллекции, тем лучше С# .
Дык это к любому языку относится. Если ты не в курсе, есть такой язык Лисп, так он вообще изначально предназначался для обработки списков. А на поверку оказался одним из самых интересных языков программирования.
GZ> Очень сомневаюсь что подобный синтаксис выйдет за пределы этого. IMHO Функциональный стиль и объекты одновременно не очень хорошо живут. Объекты с их состоянием и строгой типизированностью — ограничивают функциональный стиль. И ввод неименованных типов, по моему, как козе баян, а может и еще хуже.
Это полноейшее заблцждение. "Сменная функция" позволяет делать код настраиваемым. Конечно можно обходиться интерфейсами или абстрактными классами, но это же ведь неудобно.
ФП же как раз отлично вписывается в ООП. Ведь и сами функции могут быть методами объектов, и объекты параметрами функций. Более того функции высшего порядка позволяют абстрагировать алгоритм от конкретных типов объектов.
GZ>Причина вполне понятна. Microsoft делает язык для серверов приложений.
Microsoft слава богу делает язык общего назначения (GP-язык). А коллекции они и в Африке коллекции. Я еще не видел ЯП который мог бы обходиться без список в том или ином виде.
GZ>И коллекции немаловажная часть DSL.
А где коллекции маловажная вещь? Да и как раз для создания DSL C# подходит не лучшим образом. Будем надеяться, что только пока.
GZ>Если еще при этом, прозрачно связать с персистентностью объектов, то такая реализация DSL будет весьма здоровская. Похоже это и есть цель C# 3.0. За пределами коллекций, => по моему не катит.
Ты просто привык к одному взгляду на мир. А мир более разнообразен.
Как я уже говорил функция является отличной абстракцией. Ну, давай попробую объяснить это на простеньком примере. Предположим у нас есть некоторое вычисление содержащее повторяющиеся части (первое вхождение выделено жирным):
int x = 2;
int y = 3;
int z = 3;
int res1 = (x + 123 + z * z) * y + (x + 321 + z * z) * y
+ (x + 111 + z * z) * y + (x + 222 + z * z) * y;
Некрасиво, правда?
Что мы может сделать чтобы устранить дублирование? Правильно ввести функцию. Но это приведет к куче лишних действий и к тому, что код станет более большим и не станет сильно понятнее:
int res2 = f1(x, y, z, 123) + f1(x, y, z, 321)
+ f1(x, y, z, 111) + f1(x, y, z, 222);
...
private static int f1(int x, int y, int z, int a)
{
return (x + a + z * z) * y;
}
По сути мы избавились от дублирования слишком дорогой ценой. А вот если мы имеем возможность оперировать функциями более вольготно и если этот синтаксис будет более компактным, то мы можем получить куда более приятный результат. Вот как тоже самое можно записать с применением лямбды:
Func<int, int> f2 = a => (x + a + z * z) * y;
var res3 = f2(123) + f2(321) + f2(111) + f2(222);
Согласись это значительно лучше читается и занимает куда меньше места. Ведь функция уложилась в одну строку, да еще и ее применение стало намного компактнее.
Так что функциональный стиль при условии грамотного реализации в языке – это очень хорошо (по крайней мере для тех кто способен думать).
А проблемой которая будет препятствовать его применению скорее станет не его непонятность, а ухудшение производительности. Дело в том, что в истинно функциональном языке выражения обрабатываются еще во время компиляции. А в C# 2.0/3.0 будет производиться довольно неэффективный косвенный вызов. В данном примере будет 4 вызова. Плюс ко всему прочему будет порожден временный объект (чтобы лямбда могла получить доступ к локальным переменным x, y, z они будет превращены в поля этого объекта).
И это еще легкий случай. А ведь вложенность при функциональной декомпозиции может быть многократной. И это уже может существенно повлиять на производительность.
Я уверен, что все эти проблемы мог бы устранить оптимизирующий компилятор или JIT, но пока что этого никто не делает. А стало быть производительность столь красивых решений будет меньшей нежели аналогичных лобовых.
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>Занятно, что "длина" синтаксиса таки имеет значение
Дык ведь линий мусор еще никому не помогал.
ANS>запись вверху неконсистентна, выбивается из общей массы.
Чё?
ANS> Я вроде как за краткость, но нужно же придерживаться некого стиля. Не нравится декларация типов (вот уж от тебя не ожидал
Почему не ожидал? Это наверно ты путашь мое отношение к статической типизации и наличие явной декларации типов.
В приведенных примерах типизация остается строгой и статической. В ней применяется вывод типов. Он столь же логичен для человека как и для компилятора. А стало быть это те подробности которые в большинстве случаев можно опусить.
ANS>) — убери: ANS>
ANS>delegate(x, y) { return x * y; }
ANS>
ANS>в рамках С-шарп (именно в "рамках") понятнее чем ANS>
ANS>(x, y) => x * y
ANS>
Дык мне еще не нравится, что в анонимном методе я не могу использовать выражения (expressions) и вынужден использовать предложения (statments). Все это приводит к довольно сильному удленнению кода, а стало быт и к его замыливанию неважными фрмгментами.
Например, сравни следующие две строки:
list.Sort(delegate(KeyValuePair<string, int> x, KeyValuePair<string, int> y { return x.Value.CompareTo(y.Value); });
и:
list.Sort((x, y) => x.Value.CompareTo(y.Value));
по-моему, очевидно, что при всей непривычности синтаксиса второй вариант намного более понятен и приятен.
ЗЫ
Уверен, что после выхода C# 3.0 синтаксис delegate(...){ ... } быстро будет забыт.
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Так что итераторы и функции высшего порядка добавленные в коллекции во втором фрэймворке — это очень вреное решение.
В этом как раз я не сомневаюсь.
VD>Но на списках приемущества функционального подхода (ФП) не заканчиваются. Функции высшего порядка позволяеют производить декомпозицию кода там, где средствами ООП это или вообще нвозможно сделать, или можно, но уж очень много кода получается.
Или попросту ненужно. Дизайн вполне описывается паттернами. И оно самодостаточно(если конечно голова на плечах есть). Иногда там встречается observer, но это все равно к функц. языкам отношения не имеет.
VD>Если ты не в курсе, есть такой язык Лисп,
А мужики, то и не знают.
Только именно важно, что предназначался для коллекций. Там существует оптимизация под решения подобных графов.
GZ>> Очень сомневаюсь что подобный синтаксис выйдет за пределы этого. IMHO Функциональный стиль и объекты одновременно не очень хорошо живут. Объекты с их состоянием и строгой типизированностью — ограничивают функциональный стиль. И ввод неименованных типов, по моему, как козе баян, а может и еще хуже.
VD>Это полноейшее заблцждение. "Сменная функция" позволяет делать код настраиваемым. Конечно можно обходиться интерфейсами или абстрактными классами, но это же ведь неудобно.
Почему?
А теперь представим такую вещь. Мы делаем какой-то здоровую функциональную функциональность. Но тут у нас произошло изменение. Нам нужно использовать класс с состоянием на чтение — запись. Оптимизация графа, летит к ... маме. След., возможности чистого функционального подхода, здесь становятся практически недоступны. Либо мы юзаем лямбда, либо императив.(я в чем-то ошибаюсь?)
VD>ФП же как раз отлично вписывается в ООП. Ведь и сами функции могут быть методами объектов, и объекты параметрами функций. Более того функции высшего порядка позволяют абстрагировать алгоритм от конкретных типов объектов.
Если методы объектов выполняют ограничения ФП.
VD>А где коллекции маловажная вещь? Да и как раз для создания DSL C# подходит не лучшим образом. Будем надеяться, что только пока.
Лисповцы наступают!!! Метапрограммирование? Честно говоря, все средства есть. Хочешь генери новые классы. Хочешь используй неименованные типы(приводя их к именнованным). Чего тебе не хватает? Да, это не Лисп. Кое что, в построении отдельного языка, будет труднее сделать. Но в рамках C# описать язык вполне можно. Особенно, если это будет C# синтаксис. Он тебе недостаточен?
VD>Ты просто привык к одному взгляду на мир. А мир более разнообразен.
Грешен. VD>
VD>Func<int, int> f2 = a => (x + a + z * z) * y;
VD>var res3 = f2(123) + f2(321) + f2(111) + f2(222);
VD>
VD>Согласись это значительно лучше читается и занимает куда меньше места. Ведь функция уложилась в одну строку, да еще и ее применение стало намного компактнее.
Чудно, только это слишком простой пример. Это применение возможной только в области видимости данной процедуры. А если решение побольше, то это выливается в обычные методы.
VD>А проблемой которая будет препятствовать его применению скорее станет не его непонятность, а ухудшение производительности. Дело в том, что в истинно функциональном языке выражения обрабатываются еще во время компиляции. А в C# 2.0/3.0 будет производиться довольно неэффективный косвенный вызов. В данном примере будет 4 вызова. Плюс ко всему прочему будет порожден временный объект (чтобы лямбда могла получить доступ к локальным переменным x, y, z они будет превращены в поля этого объекта).
Именно. Не в бровь, а в глаз. delegate — это объект. Притом объект с состоянием. Ты знаешь как его можно сделать без состояния? Реформировать процедуру чтобы сохранять состояние в параметрах? Тогда получается беспредел с рекурсией(если эта шняга работает только в рамках процедуры, а то еще больше интересного). VD>И это еще легкий случай. А ведь вложенность при функциональной декомпозиции может быть многократной. И это уже может существенно повлиять на производительность.
Absolutly right. Тело функциональной программы — это граф. И его надо оптимизировать.
Что будет если написать так:
Func<int, int> f2 = a => (f2(a) + 1);
var t=f2;
VD>Я уверен, что все эти проблемы мог бы устранить оптимизирующий компилятор или JIT, но пока что этого никто не делает. А стало быть производительность столь красивых решений будет меньшей нежели аналогичных лобовых.
Мне интересен вопрос, как они это сделают?
WH>В C#2 появились nullable типы. WH>Те в данном случае будет так
Да, результирующий тип должен быть или тем же самым, для ссылочных,
или Nullable<Type> для value-type-ов.
ps
но еще раз повторяю, что данный код совсем не равносилен исходному примеру, и может выдавать некорректный результат для многопоточного приложения или для кода с side-эффектами.
WH>
VD>Для вашей проблемы сто лет назад был придуман паттерн NuulObject. То есть объект выполняющий роль того самого null. При этом он предоставлеяет некие значения которые считаются недействительными. Для ссылочных объектов это специальные синглтон-экземлпяры, для вэлью-типов предопределенные значения. Вот пример использования данного паттерна:
NullObject — совсем не тоже самое, что null, т.к. возникают проблемы с преобразованием типов, со сравнениями и т.д.
например:
interface IA{}
interface IB:IA{}
class NullableIA:IA
{
public static readonly IA Null = new NullableIA();
}
class NullableIB:IB
{
public static readonly IB Null = new NullableIB();
}
class Program
{
static void Main()
{
IA a = NullableIA.Null;
CheckForNull(a); //print null
IB b = NullableIB.Null;
CheckForNull(b); //print null
a = b;
CheckForNull(a); //print not null
}
public static void CheckForNull(IA a)
{
if (a == NullableIA.Null)
Console.WriteLine("null");
else
Console.WriteLine("not null");
}
public static void CheckForNull(IB b)
{
if (b == NullableIB.Null)
Console.WriteLine("null");
else
Console.WriteLine("not null");
}
}
как должны быть написаны методы CheckForNull, чтобы возвращать правильный результат?
ради чего программист должен писать Null-заглушки?
если использовать Null-объекты, то какие операции использовать вместо is, as и (Type)? ведь Null-объект, формально декларируя поддержку интерфейса, на самом деле же нифига не поддерживает.
и т.д.
оператор .? в применении к полям на get выглядит более менее логично
логику использования уже правильно описал wolfhound.
но код должен генериться более страшный, чем у него в примере, чтобы избежать проблем, связанных с повторным доступом к полям.
но также есть еще вопрос, можно ли применять оператор .? к set-полям/свойствам, и к функциям и процедурам.
адекватен ли код:
1.
a.?Value = 5;
2.
a.?Eval(5);
3.
int? v = a.?Execute();
имхо, адекватен, т.к. в случае вызова функции, мы по аналогии с полем на get просто возвращаем null,
а в случае — set-а или процедуры просто не выполняется правая часть.
Резюме:
1. get
код вида
<res> = <primary> ".?" <field-name>
имеет тип результата:
typeof(<primary>.<field-name>) — для ссылочных типов
и
Nullable<typeof<primary>.<field-name> — для value-типов
и код преобразуется в
var q = <primary>;
if (q != null)
<res> = q.<field-name>;
else
<res> = null;
2. set
<primary>.?<field-name> = value;
преобразуется в
var q = <primary>;
if (q != null)
q = value;
значение value не вычисляется, если .? возвращает null.
3. вызов процедуры (void метода)
код вида
<primary>.?<method-name>(<arg-1>, <arg-2>)
преобразуется в
var q = <primary>;
if (q != null)
q.<method-name>(<arg-1>, <arg-2>);
выражения arg-1 и arg-2 не вычисляются, если <primary> == null
4. вызов функции (метода, возвращающего значение):
Здравствуйте, FDSC, Вы писали:
DG>>Особенно это нужно при описании правил бизнес логики, т.к. в бизнес-логики часто мы имеем на руках только частичную информацию о мире
FDS>То же с этим очень мучаюсь. Просто жуть!
Может быть пока не вышел C# 4.0 патерн Special Case (Null Object) поможет?
Здравствуйте, DarkGray, Вы писали:
DG>как должны быть написаны методы CheckForNull, чтобы возвращать правильный результат?
Идея паттерна NullObject заключается в том, чтобы вообще исключить подобные проверки. Для тебя null вообще не существует. Если уж нужно проверять на реальность объекта, то просто сравнивашь с тем самым Null-объектом.
DG>ради чего программист должен писать Null-заглушки?
Ради того, чтобы не трахаться с провекрами и не получать случайные вылеты. Логика приложения при этом строется так, чтобы все проверки приводили к какому-то поведению принятому по умолчанию и корректному с точки зрения приложения.
DG>если использовать Null-объекты, то какие операции использовать вместо is, as и (Type)?
А причем тут is и as? Систему типов они не отменяют. Они устранят необходимость постоянно проверять все ссылки на нул.
Тебя, кстати, не угнетает, что в дотнете принят паттерн в соотвествии с которым свойства и методы возвращающие коллекции и массив всегда должны возвращать пустой массив, а не null?
DG> ведь Null-объект, формально декларируя поддержку интерфейса, на самом деле же нифига не поддерживает.
Null-объект поддерживает такой же интферйс как и другие обхекты того же класса. Просто он используется вместо null. Не придумывай лишнего.
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, GlebZ, Вы писали:
GZ>я в чем-то ошибаюсь?
Да, но чтобы это понять нужно просто попрограммировать с использованием новых возможностей, а потом попробовать жить без них.
VD>>ФП же как раз отлично вписывается в ООП. Ведь и сами функции могут быть методами объектов, и объекты параметрами функций. Более того функции высшего порядка позволяют абстрагировать алгоритм от конкретных типов объектов. GZ>Если методы объектов выполняют ограничения ФП.
Какие еще ограничения? Ты о чем?
GZ>Лисповцы наступают!!!
Лисп только один из примеров. С тем же успхом можно привести и ОКамл, и даже С++.
GZ> Метапрограммирование? Честно говоря, все средства есть.
Языком в основном. Сложность как бы не соизмеримая. Потому на практике метапрограммирования раз два и обчелся.
GZ> Хочешь генери новые классы. Хочешь используй неименованные типы(приводя их к именнованным). Чего тебе не хватает?
Удобства.
GZ> Да, это не Лисп. Кое что, в построении отдельного языка, будет труднее сделать. Но в рамках C# описать язык вполне можно. Особенно, если это будет C# синтаксис. Он тебе недостаточен?
Я не смог уловить смысла этого абзаца.
VD>>Ты просто привык к одному взгляду на мир. А мир более разнообразен. GZ>Грешен.
Вот в этом и проблема. Я тоже долго жил в этом мире, но понял, что другой мир имеет свои приемущества. Но и этот новый мир не идеален. А вот когда оба мира доступны в рамках одного языка, то жить становится удобно и приятно.
GZ>Чудно, только это слишком простой пример.
Дык, я же тебе сказал, что на более сложном примере будет только больший выигрышь.
GZ> Это применение возможной только в области видимости данной процедуры. А если решение побольше, то это выливается в обычные методы.
Нет, будет проще. Будет кое-как написанная гора кода вообще без декомпозиции. Средств то подходящих нет ведь.
GZ>Именно. Не в бровь, а в глаз. delegate — это объект.
Ну, заявление что делегат — это объект слишком натянуто. Для компилятора это всего лишь ссылка на функцию — примити.
GZ> Притом объект с состоянием.
Что? У самого делегата состояния нет. Это всго лишь указатеь. А в языке вообще ссылка на метод (безтелесная).
GZ> Ты знаешь как его можно сделать без состояния?
У него нет состояния. Состояние может быть у ассоциированного объекта, но это не одно и то же.
GZ> Реформировать процедуру чтобы сохранять состояние в параметрах? Тогда получается беспредел с рекурсией(если эта шняга работает только в рамках процедуры, а то еще больше интересного).
Есть куча работ по комбинаторной логике и по преобразованию функций. Математически доказоно, что функции можно гибко переписывать во время компиляции. Многие ФЯ делают это без проблем и достигают очень нехилой производительности.
Так что производительность упирается в качество реализации компиляторов (как языков, так и джита). И рано или поздно компиляторы дотнета научатся оптимизировать функцональный код.
GZ>Absolutly right. Тело функциональной программы — это граф. И его надо оптимизировать.
Это, извини, чушь. Функциональные программы — это программы. Их моно представить как угодно. Есть стековые машины. Есть основанные на свертке графов, но они тоже по сути используют стэк. Ну, а что до компилируемых ФЯ, то в большинстве случае код после преобразований превращается в машинный (или IL). Так что никаких пробем тут нет.
Еще раз повторюсь. Есть куча научных работ и работающие компиляторы. Возми те же SML.NET и F#. Они показывают очень высокую производительность и полностью компилируются в MSIL. Вот только если компилятор C# преобразует код в MSIL практически 1 в 1, то функциональные компиляторы перед этоим выполняют преобразование АСТ таким образом, чтобы обеспечить масимальную эффективность. Что-то оптимизируется. Какие-то методы сливаются и плучаются новые (невидимые). Где-то все переписывается настолько, что уже вообще нельзя говорить о похожести на исходную программу.
GZ>Что будет если написать так: GZ>
Для шарпа это неверная программа. Но она переписывается так:
Func<int, int> f2 = null;
f2 = a => (f2(a) + 1);
var t = f2;
и будет здесь банальный рекурсивный вызов. Ну, и так как код написан бездумно, то будет переполнение стека в виду вечной рекурсии. Линивых вычислений в Шарпе ведь нет.
А вот на Хаскеле подобная программа порождала бы список чисел. И это был бы совершенно корректной программой.
VD>>Я уверен, что все эти проблемы мог бы устранить оптимизирующий компилятор или JIT, но пока что этого никто не делает. А стало быть производительность столь красивых решений будет меньшей нежели аналогичных лобовых. GZ>Мне интересен вопрос, как они это сделают?
Ести тебе серьезно интересен этот вопрос, то советую обратиться к научным работам по этому поводу.
DG>>Extensions с полями работает? или только с методами?
DG>>т.е. можно ли через Extension добавить поле?
VD>А подумть? Как ты добавишь поле? Ты же не можешь изменять сам объект. Это только синтаксический сахар.
VD> Идея паттерна NullObject заключается в том, чтобы вообще исключить подобные проверки. Для тебя null вообще не существует. Если уж нужно проверять на реальность объекта, то просто сравнивашь с тем самым Null-объектом.
ню-ню, посмотрю я как, ты в большом проекте на каждый чих сможешь написать корректный Null-object.
Взять тот же пример с возврастом:
Как должен выглядеть Null-объект, чтобы корректно работали оба варианта:
if (user.Age > 21)
{
//bla-bla
}
if (user.Age < 16)
{
//bla-bla
}
Во-вторых:
как написать Null-объект, если исходный интерфейс не подразумевал null-возвраст?
interface IUser
{
int Age {get;}
}
class NullUser:
IUser
{
public int Age {get {return xxx;}}
}
что должно быть написано вместо xxx, чтобы корректно работали вышеприведенные условия.
VD>Тебя, кстати, не угнетает, что в дотнете принят паттерн в соотвествии с которым свойства и методы возвращающие коллекции и массив всегда должны возвращать пустой массив, а не null?
Вообще-то, в бизнес-логике null и пустой массив — это не одно и тоже
null — это "я не знаю, какие есть элементы у этого объекта"
пустой массив — это "я знаю, что элементов у объекта нет".
ps
и, вообще, авторитетом подавил, а не на один из моих вопросов, так адекватного ответа и не дал.
Здравствуйте, stalcer, Вы писали:
S>1) tmp1 = Root; S>2) tmp2 = tmp1.?Config; S>3) tmp3 = tmp2.?User; S>4) tmp4 = tmp3.?Age; S>5) tmp5 = tmp4 > 16; S>6) if (tmp5)
S>Что должно быть на каждом шаге? Какого типа каждое из этих промежуточных выражений?
S>
S>int x = Root.?Config.?User.?Age;
S>
Тип на каждом промежуточном шаге может быть любым. Но эти типы должны неявно приводиться к типу указанному слева от последовательности
Root.?Config.?User.?Age
Специально для if-выражений можно предусмотреть дополнительно преобразование T? -> bool.
Здравствуйте, VladD2, Вы писали:
VD>Почему не ожидал? Это наверно ты путашь мое отношение к статической типизации и наличие явной декларации типов.
А есть ли жизнь без интелисенса?
VD>ЗЫ
VD>Уверен, что после выхода C# 3.0 синтаксис delegate(...){ ... } быстро будет забыт.
Я пока подозреваю, что в C# нового синтаксиса не будет. Проще новый язык разработать без старого багажа. Типа Z#, а C# объявить устаревшим Это как раз вписывается в популярную в нынешней индустрии схему "раз в пять-сем лет — новый язык".
DG>к классу-то как раз и неинтересно приписывать... DG>т.к. хочется добавлять внешние методы и к готовым классам (библиотечным, чужим и т.д.).
Не, ты не понял. Я имею в виду что-то наподобие следующего:
public classextension StringExtr : string
{
// Модификатор доступа вообще говоря для расширений не нуженbool IsEmpty // Это расширение свойством.
{
get { return this != null || Length == 0; }
}
int ToInt32(int defaultValue) // Это расширение методом
{...}
}
Т.е. во-первых явно сгруппировать члены, расширяющие конкретный целевой класс (сейчас их можно смешать в один static класс Util и все.)
Во-вторых, Перенести "новый" синтаксис в определение самого класса-расширителя, а не метода.
"Условное" наследование от string означает, что this имеет тип string (хотя может быть null для reference-типов), а дополнительное ключевое слово extension подразумевает, что экземпляры StringExtr создавать нельзя (как static), и что нужно применять этот класс как набор расширений для string.
iT>Во-вторых, Перенести "новый" синтаксис в определение самого класса-расширителя, а не метода. iT>"Условное" наследование от string означает, что this имеет тип string (хотя может быть null для reference-типов), а дополнительное ключевое слово extension подразумевает, что экземпляры StringExtr создавать нельзя (как static), и что нужно применять этот класс как набор расширений для string.
но тогда придется бороться с объявлением полей внутри такого класса, создания экземпляра такого класса и т.д.
Здравствуйте, Igor Trofimov, Вы писали:
iT>Непонятно, как они с синтаксисом будут решать. Для методов получилось удачно, лишних ключевых свойств не потребовалось.. А для свойств тех же?
Вариантов много. Или тот же this куда-нибудь всунуть. Или атрибутом указать.
iT>События не очень понятно как можно расширять — только если за счет других событий целевого класса. Оно в реальности-то пригодится?
Вот народ и думат надо ли...
iT>А вот свойства и операторы — это действительно было бы полезно..
С операторами проблема. Они нужны внутри дженерик-алгоритмов которые компилирвются в мсил. А расширения это чисто синтаксический механизм. Так что смысла в них будет не очень много. Вернее может и будет. Но уж больно будет не полноценное решение.
iT>По мне, так лучше бы ввели ключевое слово и приписывали бы его к самому классу, а то как сейчас — сам класс получается вообще не при чем...
А он и не причем. Расширение может возидействовать сразу на несколько классов. Даже на интерфейсы.
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, DarkGray, Вы писали:
DG>ню-ню, посмотрю я как, ты в большом проекте на каждый чих сможешь написать корректный Null-object.
Все грамотные программися сто лет как пользуются этим паттерном. Так что приобщайся.
DG>Взять тот же пример с возврастом:
DG>Как должен выглядеть Null-объект, чтобы корректно работали оба варианта: DG>
Будет у тебя нал-объект попадать во второй диапазон. Что страшного?
DG>Во-вторых: DG>как написать Null-объект, если исходный интерфейс не подразумевал null-возвраст?
Есть такая штука — проектирование. Очень полезная вещь.
DG>Вообще-то, в бизнес-логике null и пустой массив — это не одно и тоже
Ты видимо читашь что-то свое. Попробуй еще раз и не думай в этот момент о постороннем.
DG>ps DG>и, вообще, авторитетом подавил, а не на один из моих вопросов, так адекватного ответа и не дал.
Никто тебя ничем не довил. Просто ты воспринимашь данный паттерн в штыки и намеренно боришся с ним. А если нет желания принимать что-то нове, то никто тебя не убидит.
Попробуй на практике. Потом поделишся результатами.
Как и любой паттерн этот не безупречен и имеет свои недостатки. Под них нужно подстараиваться. Бывает что паттерн не применим или неудобен. Что же поделашь?
Я стараюсь вообще избегать ситуаций когда null являтся шатной ситуацией. Это разко упрощает код. Те же нал-объекты нужны тольк там где возможно отсуствие объекта. А во многих случаях это просто не нужно. В том же конфиге объект просто обязан присуствовать. Зачем там null?
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>А есть ли жизнь без интелисенса?
А кто тебе сказал, что его не будет? Вывод типов ведь есть? А занчит есть и типы. А раз они есть, то будет и ителисенс. Пока что его нет и это сильно напрягает. Но всему свое время.
ANS>Я пока подозреваю, что в C# нового синтаксиса не будет.
Да он уже есть. И я не вижу чтобы кто-то хотел от него отказаться.
ANS> Проще новый язык разработать без старого багажа.
Не, ну, нужно все же более трезво смотреть на реальность. Что проще? Он уже есть!
ANS> Типа Z#, а C# объявить устаревшим Это как раз вписывается в популярную в нынешней индустрии схему "раз в пять-сем лет — новый язык".
Дык есть C# 1.х, есть C# 2.х, и будет C# 3.х. Это три разных языка имеющие обратную совместимость сверху вниз. При этом ты можешь писать на C# 1.х используя компилятор и среду от 3.х. Что еще нужно? Тебя же никто не заставляет использовать неудобные или непонятные по твоему мнению констркции?
Я думаю, синтаксис лямбд уже не изменится. Хорошо бы чтобы анонимные типы стали полноценными tuple-ами (т.е. типами которые можно сравнивать при совпадении структуры). Так же хотелось бы, чтобы компилятор научился делать преобразования лямбд во время компиляции. Еще было бы здорово если деревья выражения можно было бы в рантайме и компайлтайме преобразовывать в код (после модификации). Все это дало бы неслыханную гибкость при впролне простом синтаксисе и позволило бы стать C#-у безусловным лидером среди языков общего назначения.
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
DG>но тогда придется бороться с объявлением полей внутри такого класса, создания экземпляра такого класса и т.д.
Это как раз для разработчиков языка и не сложно. Самое сложное — это а) проработать семантику — какая она должны быть, чтобы не было каких-нибудь нежелательных побочных эффектов и б) придумать синтаксис, такой, чтобы сохранить совместимость и не слишком усложнить все.
VD> Будет у тебя нал-объект попадать во второй диапазон. Что страшного?
страшно то, что результат должен был быть не такой.
если это было бизнес-правило, например, для интернет-магазина:
Если возвраст < 10, то показать рекламу детских игрушек.
то пользователях, которые не указали возраст будет показывать не интересная им реклама, что для интернет магазина означает недополучение прибыли.
И это ты называешь не страшно?
VD>Попробуй на практике. Потом поделишся результатами.
Пробовал. Очень громоздко и неудобно.
Кол-во всего кода возврастает на четверть.
Сопровождение усложняется раза в полтора, т.к. при изменении основного объекта, надо также менять и Null-ипостаси.
Речь идет о том, что Null-паттерн в C# реализовывать неудобно, в SmallTalk-е удобно, а в C# — неудобно.
Поэтому и нужен специальный синтаксический сахар для работы с нулевыми значениями.
Здравствуйте, VladD2, Вы писали:
VD>Дык есть C# 1.х, есть C# 2.х, и будет C# 3.х. Это три разных языка имеющие обратную совместимость сверху вниз. При этом ты можешь писать на C# 1.х используя компилятор и среду от 3.х. Что еще нужно? Тебя же никто не заставляет использовать неудобные или непонятные по твоему мнению констркции?
Да мне то, как раз, всё равно. А бедному Васе Пупкину *всё* нужно будет выучить. Ибо на собеседовании могут спросить...
Кстати, это бы еще позволило расширять статические члены класса.
Например, "засунуть" в DateTime статическое свойство TimeOfDinner Или расширить какой-то enum более дружественным, типизированным методом Parse.
Вообще позволило бы чуть-чуть "дописывать" стандартную библиотеку под нужды системы. Сахар, конечно, но очень сладкий и в целом безопасный.
Здравствуйте, VladD2, Вы писали:
VD>>>ФП же как раз отлично вписывается в ООП. Ведь и сами функции могут быть методами объектов, и объекты параметрами функций. Более того функции высшего порядка позволяют абстрагировать алгоритм от конкретных типов объектов. GZ>>Если методы объектов выполняют ограничения ФП. VD>Какие еще ограничения? Ты о чем?
Пока оставлю эту тему. Только ламеры читают help. Нескольких часов мутузанья клавиатуры и компилятора, не дали результата. А сейчас заглянул в спецификацию:
Note
The PDC 2005 Technology Preview compiler does not support lambda expressions with a statement block body. In cases where a statement block body is needed, the C# 2.0 anonymous method syntax must be used.
Так что, пока беру слова назад.
GZ>> Хочешь генери новые классы. Хочешь используй неименованные типы(приводя их к именнованным). Чего тебе не хватает? VD>Удобства.
R# тебя спасет?
GZ>> Да, это не Лисп. Кое что, в построении отдельного языка, будет труднее сделать. Но в рамках C# описать язык вполне можно. Особенно, если это будет C# синтаксис. Он тебе недостаточен? VD>Я не смог уловить смысла этого абзаца.
DSL — слишком абстрактное понятие. Не хотелось бы затевать флейм (который, к тому-же, уже был здесь не раз).
VD>>>Ты просто привык к одному взгляду на мир. А мир более разнообразен. GZ>>Грешен. VD>Да, но чтобы это понять нужно просто попрограммировать с использованием новых возможностей, а потом попробовать жить без них. VD>Вот в этом и проблема. Я тоже долго жил в этом мире, но понял, что другой мир имеет свои приемущества. Но и этот новый мир не идеален. А вот когда оба мира доступны в рамках одного языка, то жить становится удобно и приятно.
Напоминает слова проповедника.
Я жил в темном мире, занимался сексом, курил, пил, дизассемблировал PowerBuilder и писал классы. Но однажды во сне ко мне пришел Haskell Curry и сказал — лямбда есть комбинаторика, и я прозрел. Я бросил старый темный мир погрязший в грехе. Теперь я живу в светлом мире, отшельником в келье среди лесной чащи, ем один горох, и пишу на Лисп. Вот оно счастье нирваны.
GZ>>Именно. Не в бровь, а в глаз. delegate — это объект. VD>Ну, заявление что делегат — это объект слишком натянуто. Для компилятора это всего лишь ссылка на функцию — примити. GZ>> Притом объект с состоянием. VD>Что? У самого делегата состояния нет. Это всго лишь указатеь. А в языке вообще ссылка на метод (безтелесная). GZ>> Ты знаешь как его можно сделать без состояния? VD>У него нет состояния. Состояние может быть у ассоциированного объекта, но это не одно и то же.
Имеется ввиду именно анонимный делегат. А у него есть состояние в виде локальных переменных. А следовательно, это нужно решать, старая реализация в виде анонимных делегатов мне, кажется, не подойдет.
GZ>> Реформировать процедуру чтобы сохранять состояние в параметрах? Тогда получается беспредел с рекурсией(если эта шняга работает только в рамках процедуры, а то еще больше интересного). VD>Есть куча работ по комбинаторной логике и по преобразованию функций. Математически доказоно, что функции можно гибко переписывать во время компиляции. Многие ФЯ делают это без проблем и достигают очень нехилой производительности. VD>Так что производительность упирается в качество реализации компиляторов (как языков, так и джита). И рано или поздно компиляторы дотнета научатся оптимизировать функцональный код.
Ага. Правда при условии, если фукнции можно представить как лямбда-термы. Иначе возможности оптимизации существенно сужаются. А у нас тут в statement block body может твориться все что угодно.
GZ>>Absolutly right. Тело функциональной программы — это граф. И его надо оптимизировать. VD>Это, извини, чушь. Функциональные программы — это программы. Их моно представить как угодно. Есть стековые машины. Есть основанные на свертке графов, но они тоже по сути используют стэк. Ну, а что до компилируемых ФЯ, то в большинстве случае код после преобразований превращается в машинный (или IL). Так что никаких пробем тут нет.
Хорошо, скажу по другому. Хотелось бы оптимизировать.
VD>и будет здесь банальный рекурсивный вызов. Ну, и так как код написан бездумно, то будет переполнение стека в виду вечной рекурсии. Линивых вычислений в Шарпе ведь нет.
Поскольку нет оптимизации как таковой, то и ленивых вычислений(в самом высоком смысле этого слова) нет. IMHO А вот то что рекурсия так долбануто сделана — это плохо. Я сразу что-то прогнал про это, потому и спросил. Испугался что ее вообще нет.
GZ>>Мне интересен вопрос, как они это сделают? VD>Ести тебе серьезно интересен этот вопрос, то советую обратиться к научным работам по этому поводу.
Не-а. Я тоже послать могу. Мне нужна обзорная статья по оптимизации в функциональных языках. Чтобы прочел, и сразу просветленный. Сразу бы бросил пить и начал бы жрать горох. (а то мои знания ограничены "примерным" понимание graph rewrite ).
Здравствуйте, DarkGray, Вы писали:
VD>> Будет у тебя нал-объект попадать во второй диапазон. Что страшного?
DG>страшно то, что результат должен был быть не такой.
Проектируй так чтобы должен был бы быть такой.
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Andrei N.Sobchuck, Вы писали:
ANS>Да мне то, как раз, всё равно. А бедному Васе Пупкину *всё* нужно будет выучить. Ибо на собеседовании могут спросить...
От лишних знаний еще никто не погибал. А то что ламеры будут отсеиваться — это хорошо.
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, GlebZ, Вы писали:
GZ>R# тебя спасет?
R# это исследование. К тому же тесная интеграция с языком.
GZ>Напоминает слова проповедника.
GZ>Я жил в темном мире, занимался сексом, курил, пил, дизассемблировал PowerBuilder и писал классы. Но однажды во сне ко мне пришел Haskell Curry и сказал — лямбда есть комбинаторика, и я прозрел. Я бросил старый темный мир погрязший в грехе. Теперь я живу в светлом мире, отшельником в келье среди лесной чащи, ем один горох, и пишу на Лисп. Вот оно счастье нирваны. GZ>
Не не так. Теперь у меня есть два мира, так что я занимаюсь сексом, курю, пью и т.п. но еще и колюсь.
GZ>Имеется ввиду именно анонимный делегат.
Метод, наверно.
GZ> А у него есть состояние в виде локальных переменных. А следовательно, это нужно решать, старая реализация в виде анонимных делегатов мне, кажется, не подойдет.
Состояние есть у любого метода. Так что ничго нового.
GZ>Ага. Правда при условии, если фукнции можно представить как лямбда-термы. Иначе возможности оптимизации существенно сужаются. А у нас тут в statement block body может твориться все что угодно.
Компилятор приципиально оперирует AST. Так что никаких проблем у него нет. Деревья выражений — это и есть AST. Так что все пучком.
GZ>Хорошо, скажу по другому. Хотелось бы оптимизировать.
Со временем будут и оптимизаторы. Про Феникс слышал? Вот в нем будут еще те возможности по оптимизации. Да и на уровне ЯП можно все что нужно сделать.
VD>>и будет здесь банальный рекурсивный вызов. Ну, и так как код написан бездумно, то будет переполнение стека в виду вечной рекурсии. Линивых вычислений в Шарпе ведь нет. GZ>Поскольку нет оптимизации как таковой, то и ленивых вычислений(в самом высоком смысле этого слова) нет. IMHO
Это не связанные вещи. Линиые же вычисления легко достигаются за счет использования итераторов.
List<int> list = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int i = 0;
var lazyList =
from elem in list
select ++i;
Console.WriteLine("i = " + i);
foreach (int x in lazyList)
Console.WriteLine("x = " + x + " i = " + i);
Console.WriteLine();
foreach (int x in lazyList)
{
if (x > 15)
break;
Console.WriteLine("x = " + x + " i = " + i);
}
Console.WriteLine();
foreach (int x in lazyList)
Console.WriteLine("x = " + x + " i = " + i);
Результат:
i = 0
x = 1 i = 1
x = 2 i = 2
x = 3 i = 3
x = 4 i = 4
x = 5 i = 5
x = 6 i = 6
x = 7 i = 7
x = 8 i = 8
x = 9 i = 9
x = 10 i = 10
x = 11 i = 11
x = 12 i = 12
x = 13 i = 13
x = 14 i = 14
x = 15 i = 15
x = 17 i = 17
x = 18 i = 18
x = 19 i = 19
x = 20 i = 20
x = 21 i = 21
x = 22 i = 22
x = 23 i = 23
x = 24 i = 24
x = 25 i = 25
x = 26 i = 26
GZ>>>Мне интересен вопрос, как они это сделают? VD>>Ести тебе серьезно интересен этот вопрос, то советую обратиться к научным работам по этому поводу. GZ>Не-а. Я тоже послать могу. Мне нужна обзорная статья по оптимизации в функциональных языках. Чтобы прочел, и сразу просветленный. Сразу бы бросил пить и начал бы жрать горох. (а то мои знания ограничены "примерным" понимание graph rewrite ).
Я закладок не делал. Если тебе в лом задать вопрос самому, то могу для тебя это сделать.
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, Igor Trofimov, Вы писали:
iT>Кстати, это бы еще позволило расширять статические члены класса. iT>Например, "засунуть" в DateTime статическое свойство TimeOfDinner Или расширить какой-то enum более дружественным, типизированным методом Parse.
А чем хуже так:
static class EnumEx
{
public static T Parse<T>(string str)
{
return (T)Enum.Parse(typeof(T), str, true);
}
}
... << RSDN@Home 1.2.0 alpha rev. 611>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
VladD2 wrote:
> ANS>Да мне то, как раз, всё равно. А бедному Васе Пупкину *всё* нужно > будет выучить. Ибо на собеседовании могут спросить... > От лишних знаний еще никто не погибал. А то что ламеры будут > отсеиваться — это хорошо.
Здравствуйте, DarkGray, Вы писали:
DG>Да, результирующий тип должен быть или тем же самым, для ссылочных, DG>или Nullable<Type> для value-type-ов.
Точно, точно!
DG>но еще раз повторяю, что данный код совсем не равносилен исходному примеру, и может выдавать некорректный результат для многопоточного приложения
DG>>но еще раз повторяю, что данный код совсем не равносилен исходному примеру, и может выдавать некорректный результат для многопоточного приложения DG>>или для кода с side-эффектами.
S>Это почему?
Потому что при такой записи вызовы свойств происходят несколько раз, поэтому при первом вызове может быть не null, а при втором вызове уже null, такое возможно если мы работаем с многопоточным приложением (и какой-то другой поток сбросил свойство), или с кодом, который имеет side-эффекты (и меняет значение свойства во время вызова)
допустим у нас есть свойство Name с side-эффектом (которое, то устанавливает свойство, то его сбрасывает) в классе User
public string Name
{
get
{
if (_Name == null)
_Name = "Вася";
else
_Name = null;
return _Name;
}
}
string _Name;
тогда при вызове вот такого кода мы будет падать по NullException-у с вероятностью 50%:
Здравствуйте, stalcer, Вы писали: S>А при чем здесь исключения. null есть только для ссылочных типов. А, например, возмем структуру Address. Пусть адрес может быть не задан по условию задачи. И что?
Nullable<Address>. И все.
Оператор .? работает только на ссылочных типах и на value типах реализующих INullable. Попытка применить его к Address вызовет compile-time error.
... << RSDN@Home 1.1.4 stable rev. 510>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Sinclair, Вы писали:
S>Nullable<Address>. И все. S>Оператор .? работает только на ссылочных типах и на value типах реализующих INullable. Попытка применить его к Address вызовет compile-time error.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Cyberax, Вы писали:
C>>Неужели вы стали фанатом С++???!!!!
VD>Я уже пережил эту стадию... лет 5 назад.
Известная история. Тот, кто недавно отрубал головы драконам, сейчас сам им стал.
Здравствуйте, VladD2, Вы писали:
VD>Я думаю, синтаксис лямбд уже не изменится. Хорошо бы чтобы анонимные типы стали полноценными tuple-ами (т.е. типами которые можно сравнивать при совпадении структуры).
Tuple возможны на рантайме .NET2
Что то типа такого
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1
{
class RefTuple<T, U>
{
public T First;
public U Second;
public override string ToString()
{
return"{" + First.ToString() + ";" + Second.ToString() + "}";
}
public override bool Equals(object obj)
{
RefTuple<T, U> that = obj as RefTuple<T, U>;
if (that == null)
return false;
return Equals(this, that);
}
public override int GetHashCode()
{
return First.GetHashCode() ^ Second.GetHashCode();
}
private static bool Equals(RefTuple<T, U> l, RefTuple<T, U> r)
{
return l.First.Equals(r.First) && l.Second.Equals(r.Second);
}
public static bool operator ==(RefTuple<T, U> l, RefTuple<T, U> r)
{
return Equals(l, r);
}
public static bool operator !=(RefTuple<T, U> l, RefTuple<T, U> r)
{
return !Equals(l, r);
}
}
struct ValueTuple<T, U>
{
public T First;
public U Second;
public override int GetHashCode()
{
return First.GetHashCode() ^ Second.GetHashCode();
}
public override string ToString()
{
return First.ToString() + ";" + Second.ToString();
}
}
class Program
{
static RefTuple<int, ValueTuple<float, ValueTuple<float, string>>> Test()
{
RefTuple<int, ValueTuple<float, ValueTuple<float, string>>> t = new RefTuple<int, ValueTuple<float, ValueTuple<float, string>>>();
t.First = 1;
t.Second.First = 1.1f;
t.Second.Second.First = 123;
t.Second.Second.Second = "asdf";
return t;
}
static void Main()
{
RefTuple<int, ValueTuple<float, ValueTuple<float, string>>> t1 = Test();
RefTuple<int, ValueTuple<float, ValueTuple<float, string>>> t2 = Test();
// t1.Second.First = 1;
Console.WriteLine(t1 == t2);
Console.WriteLine(t1);
Console.WriteLine(t2);
}
}
}
Осталось прикрутить синтаксический сахар както так:
Кстати, любопытно, что в LINQ Preview, в Query.dll вводится атрибут System.Runtime.CompilerServices.ExtensionAttribute, который прицеплен ко всем операциям над последовательностями (класс System.Query.Sequence). Кажется именно это и является для комплятора признаком метода-расширения.
Здравствуйте, Дарней, Вы писали:
Д>Здравствуйте, DarkGray, Вы писали:
DG>>До сих пор не хватает "красивой" работы с нулевыми значениями
Д>А может быть, вместо null надо использовать специальный "нулевой" объект для каждого типа? Например, для массивов он будет возвращать Count = 0
В Net стараются внедрять статическое свойство empty
... << RSDN@Home 1.1.4 stable rev. 510>>
и солнце б утром не вставало, когда бы не было меня
AVK>А возможности подключения реализаций опять нет
Разговаривал сегодня на эту тему с Хейлсбергом. Итог таков — они много думали про подобное, но посчитали что это слишком тяжело для восприятия, посему ничего в таком направлении не сделали и перспектив на ближайшее будущее никаких.
Здравствуйте, AndrewVK, Вы писали:
AVK>Вышел первый прообраз спецификации.
Жаль... Задумка была хорошей, но похоже обречена....
Вся команда ObjectSpaces перешла в LINQ.
Здравствуйте, AndrewVK, Вы писали:
AVK>Разговаривал сегодня на эту тему с Хейлсбергом. Итог таков — они много думали про подобное, но посчитали что это слишком тяжело для восприятия, посему ничего в таком направлении не сделали и перспектив на ближайшее будущее никаких.
А ты ему не додумался дать в качестве идеи Скалу?
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, DarkGray, Вы писали:
VD>> Будет у тебя нал-объект попадать во второй диапазон. Что страшного?
DG>страшно то, что результат должен был быть не такой.
DG>если это было бизнес-правило, например, для интернет-магазина: DG>Если возвраст < 10, то показать рекламу детских игрушек.
DG>то пользователях, которые не указали возраст будет показывать не интересная им реклама, что для интернет магазина означает недополучение прибыли.
DG>И это ты называешь не страшно?
А на самом деле, что должно показываться таким пользователям?
Третий вид рекламы?
if (user.Age > 21)
{
//bla-bla
}
if (user.Age < 16 && user.Age > 0)
{
//bla-bla
}
Реклама для взрослых?
Тогда дефолтное значение можно взять заведомо большое, типа int.MaxValue.
ie>А на самом деле, что должно показываться таким пользователям? ie>Третий вид рекламы? ie>Реклама для взрослых?
Может быть ничего, может какая-то другая реклама, может еще что-то.
В том-то и дело, что я не хочу об этом думать, когда пишу вышеперечисленные правила, я хочу иметь атомарность при внесении новых правил, изменении старых и т.д..
Если же я завожу какие-то NullObject-ы, default-ные MinValue, MaxValue — то у меня эта атомарность пропадает.
Вышеперечисленные правила, вообще, могут собираться из разных источников — их может быть даже заводят разные люди (причем может быть даже не совсем программисты).
И поэтому, в общем, случае мы, вообще, не знаем какие у нас есть правила, и какой NullObject (default-value) лучше всего подсунуть каждому правилу, или сразу всем правилам.
ie>Тогда дефолтное значение можно взять заведомо большое, типа int.MaxValue.
Чтобы сделать такое предположение, я должен иметь полное представление о том, какие правила у меня есть сейчас, будут завтра.
А зачем мне тратить свое время на то, чтобы такое знание получить?
А потом подумалось: если раньше глядя на такой код, ты можешь быть уверен, что в в случае param == null вылетит NullReferenceException, то теперь эта уверенность пропадает. С одной стороны "ООП всегда! ООП везде! ООП на радость нам!", а с другой стороны, какой-то не правильный ООП получается
Здравствуйте, VladD2, Вы писали:
VD>Вы бы им там сказали, что писат select в конце запросы — это по меньше мере странно (это я про квари компрехэншон).
Все програссивное теловетчество наоборот, плачет из-за того что select находится впереди, а не позади from. Приходится вторым проходом проверять семантику селекта, потому как низзя узнать семантически что же здесь хотели юзвери без полей из from.
Здравствуйте, VladD2, Вы писали:
VD>Вы бы им там сказали, что писат select в конце запросы — это по меньше мере странно (это я про квари компрехэншон).
Там, если копнуть поглубже, странного намного больше. На самом деле как SQL он только выглядит на совсем простых запросах. А как чего посложнее с участием нескольких таблиц, так больше начинает смахивать на эдакий гибрид sql и xpath (а в васике вобще, уроды, в запросе xml используют).
Здравствуйте, GlebZ, Вы писали:
GZ>Все програссивное теловетчество наоборот, плачет из-за того что select находится впереди, а не позади from.
Можно пальцем на это "Все програссивное теловетчество"?
GZ>Приходится вторым проходом проверять семантику селекта, потому как низзя узнать семантически что же здесь хотели юзвери без полей из from.
Это ты про проходы людских глаз? Или ты беспокошся за эффективность компиляторов? Если, за последнее, то лучше не беспокойся. У компиляторов проблем нет. Они один хрен сначала AST создают.
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, AndrewVK, Вы писали:
AVK>Там, если копнуть поглубже, странного намного больше. На самом деле как SQL он только выглядит на совсем простых запросах. А как чего посложнее с участием нескольких таблиц, так больше начинает смахивать на эдакий гибрид sql и xpath (а в васике вобще, уроды, в запросе xml используют).
Дык это то понятно. Все же иерархические запросы, хотя как я понял из примеров и джоины там есть как в старом SQL и вообще все очень даже похоже.
Но select сздани — это полнейший изврат. Читашь и чувствуешь себя дебилом. А вся аргументация про области видимости выглядит просто смешно, да и в Жлабэйсике то сделали по человечески.
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
ie>А потом подумалось: если раньше глядя на такой код, ты можешь быть уверен, что в в случае param == null вылетит NullReferenceException, то теперь эта уверенность пропадает. С одной стороны "ООП всегда! ООП везде! ООП на радость нам!", а с другой стороны, какой-то не правильный ООП получается
+1
... << RSDN@Home 1.2.0 alpha rev. 618>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.