Re[5]: out- и ref-параметры.
От: _FRED_ Черногория
Дата: 24.02.10 11:41
Оценка:
Здравствуйте, _FRED_, Вы писали:

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


_FR>А если так…


Вот только правильно реализацию сделать так (а работа с bool принесёт неприятные сбрпризы):
  Скрытый текст
[Serializable]
public struct TryResult<T>
{
  private readonly bool success;
  private readonly T result;

  public TryResult(T value) {
    success = true;
    result = value;
  }

  public bool IsSuccess {
    [DebuggerStepThrough]
    get { return success; }
  }

  public T Result {
    [DebuggerStepThrough]
    get {
      if(!IsSuccess) {
        const string Message = "TryResult object must have a result.";
        throw new InvalidOperationException(Message);
      }//if

      return result;
    }
  }

  public T GetResultOrDefault() {
    return result;
  }

  public T GetResultOrDefault(T defaultResult) {
    return IsSuccess ? result : defaultResult;
  }

  public static bool operator true(TryResult<T> value) {
    return value.IsSuccess;
  }

  public static bool operator false(TryResult<T> value) {
    return !value;
  }

  public static bool operator !(TryResult<T> value) {
    return !value.IsSuccess;
  }

  public static implicit operator TryResult<T>(T value) {
    return new TryResult<T>(value);
  }

  public static explicit operator T(TryResult<T> value) {
    return value.Result;
  }
}
Help will always be given at Hogwarts to those who ask for it.
Re[3]: out- и ref-параметры.
От: Undying Россия  
Дата: 24.02.10 11:42
Оценка:
Здравствуйте, fmiracle, Вы писали:

F>
F>ConvertionResult<int> result = int.TryParse( "asdf" );
F>if( !result.IsValid )
F>{
F>    Console.WriteLine("It was not an integer number");
F>    return;
F>}
F>Console.WriteLine( result.Value );
F>


И чем это принципиально лучше варианта с out? По мне твой вариант и вариант с out по читабельности равнозначны. Но в общем случае out'ы гибче, например, если понадобиться возвращать также сообщение об ошибке, то в варианте с out будет достаточно добавить перегрузку bool TryParse(out int result, out string error), а при твоем подходе придется писать класс ConvertionResultWithError<T>. Иногда создание дополнительного класса оправдано, конечно, но далеко не всегда. И вот для тех случаев, когда дополнительный класс себя не оправдывает, out'ы в шарпе очень полезное и удачное решение, которое никак не заслуживает оценки code-smell.
Re[5]: out- и ref-параметры.
От: Undying Россия  
Дата: 24.02.10 11:47
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>
_FR>var result = TryParse("");
_FR>if(result) {
_FR>  Func(result.Result);
_FR>}//if
_FR>


Идея интересная, но по мне нафиг такие трюки. Неочевидная перегрузка операторов это конец читабельности.
Re[6]: out- и ref-параметры.
От: _FRED_ Черногория
Дата: 24.02.10 11:51
Оценка:
Здравствуйте, Undying, Вы писали:

_FR>>var result = TryParse("");
_FR>>if(result) {
_FR>>  Func(result.Result);
_FR>>}//if


U>Идея интересная, но по мне нафиг такие трюки. Неочевидная перегрузка операторов это конец читабельности.


Что же там неочевидного? Как раз-таки исходя из очевидной семантики результата перегрузка операторов никого не может ввести в заблуждение.
Help will always be given at Hogwarts to those who ask for it.
Re[4]: out- и ref-параметры.
От: Mr.Cat  
Дата: 24.02.10 11:55
Оценка: +1
Здравствуйте, Undying, Вы писали:
U>Да и даже с паттерн-матчингом
U>(result, value) = int.TryParse(text);

Ничего вы не понимаете в колбасных обре^W^W паттерн-матчинге.
Ежели у нас
tryParse :: String -> Maybe Int

то
case tryParse text of
    Just value -> -- do something to `value'
    Nothing -> -- OH, SHI~

А ежели у нас "do something to `value'" заключается в применении функции
f :: Int -> Maybe a

то достаточно:
tryParse text >>= f


PS: ghci под рукой нет, но что-то в таком духе.

http://www.haskell.org/all_about_monads/html/index.html
http://www.haskell.org/all_about_monads/html/maybemonad.html
Re[5]: out- и ref-параметры.
От: Undying Россия  
Дата: 24.02.10 12:03
Оценка: +1 -5
Здравствуйте, Mr.Cat, Вы писали:

MC>Ничего вы не понимаете в колбасных обре^W^W паттерн-матчинге.

MC>Ежели у нас
MC>
MC>tryParse :: String -> Maybe Int
MC>

MC>то
MC>
MC>case tryParse text of
MC>    Just value -> -- do something to `value'
MC>    Nothing -> -- OH, SHI~
MC>

MC>А ежели у нас "do something to `value'" заключается в применении функции
MC>
MC>f :: Int -> Maybe a
MC>

MC>то достаточно:
MC>
MC>tryParse text >>= f
MC>


Как хорошо что в C# нет паттерн-матчинга.
Re[6]: out- и ref-параметры.
От: Mr.Cat  
Дата: 24.02.10 12:05
Оценка:
Здравствуйте, Undying, Вы писали:
U>Как хорошо что в C# нет паттерн-матчинга.
Jedem das Seine
Re[2]: out- и ref-параметры.
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 24.02.10 12:19
Оценка:
Здравствуйте, Undying, Вы писали:

U>Важное достоинство out, он полностью отвечает принципу самодокументируемости кода. Результат функции с out'ами понятен, даже без чтения документации.


U>
U>bool Read(out string dataBuffer, out uint remainDataCount);
U>


U>А вот если ее переписать на тупл:


U>
U>Tuple<bool, string, uint> Read();
U>


U>То без поллитры определить, что значит, к примеру, uint будет затруднительно.


Ты пытаешься писать больше в процедурном ключе. В объектном все-таки правильнее будет выделить класс Stream, где будут поля Size, Position, ...
Re[4]: out- и ref-параметры.
От: 0x7be СССР  
Дата: 24.02.10 12:23
Оценка:
Здравствуйте, Undying, Вы писали:

U>Какой код на C# 3.5 ты пишешь вместо int.TryParse(text, out value)?

Начнем с того, что я функцией TryParse вообще стараюсь не пользоваться. Вылет исключения при попытке распарсить что-то меня обычно устраивает. Далее, если по-простому, то вместо функции "Try-что-нибудь", мне обычно нужна функция "Can-что-нибудь" в совокупности с функцией "Do-что-нибуть". Хотя на практике у меня не возникало необходимости использовать парсинг таким образом, я бы сделал так: завел бы функцию "CanParse", оборачивающую TryParse и использовал бы её в совокупности с Parse.

Выглядело бы это так:
var myInt = int.CanParse(MyString) ? int.Parse(MyString) : DefaultBusinessLogicAcceptableIntValue;

или
if(!int.CanParse(MyString))
{
  throw new ...
}
var myInt = int.Parse(MyString);

Казалось бы, разница невелика, но:
1. переменная myInt всегда будет проинициализирована ТОЛЬКО корректным распарсенным значением. Там никогда не будет "дефолтового" нуля, который случайно может попасть в дальнейший код, как легальное значение.
2. этот вариант удобнее тем, что функция возвращает значение только как результат. В итоге ее удобнее комбинировать с другими функциями через функции высшего порядка (тот же Linq). Например:
var myInts = MyStringsCollection.Where(int.CanParse).Select(int.Parse);


C TryParse этот код будет таким:
IEnumerable<int> ParseStrings(IEnumerable<string> strings)
{
  foreach(var i in strings)
  {
    int result;
    if(int.TryParse(i, out result))
    {
      yield return result;
    }
  }
}
...

var mySrtings = ParseStrings(strings);

Неуклюже, правда?

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

Например, это могло бы выглядеть так (реализация optional тут взята с потолка, метод Unwap принимает функцию, которую надо вызвать, если значения нет):
var myInt = int.OptionalParse(MyString).Unwrap(() => throw new ...);

и для коллекций:
var myInts = MyStringsCollection.Select(int.OptionalParse).Select(_ => _.Unwrap(() => throw new ...));

При этом метод парсинга вызывается один раз и перформанс не страдает.

Вариантов реализации того, во что и как заворачивать опциональный результат функции — тысяча и один, но принцип у них всех похож.
Re[3]: out- и ref-параметры.
От: Undying Россия  
Дата: 24.02.10 12:46
Оценка:
Здравствуйте, Mystic, Вы писали:

M>Ты пытаешься писать больше в процедурном ключе. В объектном все-таки правильнее будет выделить класс Stream, где будут поля Size, Position, ...


Правильно писать в ключе наиболее подходящем для решения задачи. Обычно наилучшим подходом для решения задачи является смесь объектного и процедурного стиля.
Re[2]: out- и ref-параметры.
От: 0x7be СССР  
Дата: 24.02.10 12:54
Оценка:
Здравствуйте, Undying, Вы писали:

U>...

U>То без поллитры определить, что значит, к примеру, uint будет затруднительно.
С этим трудно поспорить. Замечу лишь, что туплы с небольшим количеством значений обычно трудностей не вызывают.
В приведенном примере, скорее всего, возвращался бы тип Optional<Tuple<string, uint>>, т.е. тупл состоял бы из двух типов. Если же надо возвращать что-то бОльшее, то можно и класс под это дело завести. 25 значений и через out-параметры плохо пролезут.
Re[2]: out- и ref-параметры.
От: SV.  
Дата: 24.02.10 13:19
Оценка: -1
Здравствуйте, MozgC, Вы писали:

MC>Цитирую из книги Framework Design Guidelines:


Обожаю такую аргументацию:

...requires experience...
...understandin...
...is not widely understood...


Извините, это инженеры, или кто? Если человек не имеет экспириенса и/или не понимает, что это за рефауты такие, и не может за пять минут разобраться с гуглем в руках, думаете, он напишет код лучше, если не провоцировать его на их использование? Лох — это судьба, говорили в городе, где я прожил много лет (на третьем месте в криминальном рейтинге городов России). Перефразируя, если человек тупой, он найдет, где ему поиметь свой факап. Без рефов и аутов. Методом проб и ошибок я пришел к выводу, что не надо а) работать на тупых менеджеров б) давать работу тупым менеджерам/инженерам. Вот и все.

Что касается исходного тезиса ("дело нехорошее"). Пока в языке нет возможности возвращать более одного значения, код с рефаутами остается наиболее элегантным, если потенциальный новый тип не нужен сам по себе (используется только для заворачивания аргументов).
Re[3]: out- и ref-параметры.
От: MozgC США http://nightcoder.livejournal.com
Дата: 24.02.10 13:35
Оценка: 1 (1) +3
Здравствуйте, SV., Вы писали:


SV.>Извините, это инженеры, или кто? Если человек не имеет экспириенса и/или не понимает, что это за рефауты такие, и не может за пять минут разобраться с гуглем в руках, думаете, он напишет код лучше, если не провоцировать его на их использование? Лох — это судьба, говорили в городе, где я прожил много лет (на третьем месте в криминальном рейтинге городов России). Перефразируя, если человек тупой, он найдет, где ему поиметь свой факап. Без рефов и аутов. Методом проб и ошибок я пришел к выводу, что не надо а) работать на тупых менеджеров б) давать работу тупым менеджерам/инженерам. Вот и все.

SV.>Что касается исходного тезиса ("дело нехорошее"). Пока в языке нет возможности возвращать более одного значения, код с рефаутами остается наиболее элегантным, если потенциальный новый тип не нужен сам по себе (используется только для заворачивания аргументов).

Начну с того, что процитированные выше правила взяты из книги Framework Design Guidelines, т.е. они больше подходят для разработки публичных API. При разработке публичных API/фреймворков нужно руководствоваться определенными принципами, например:
— стремиться к простоте
— обеспечить низкий входной порог программистов
— разрабатывать библиотеку для широкого круга программистов
— затруднить неправильное использование API
и т.д.
Параметры out и ref не очень способствуют выполнению этих принципов. Я практически уверен, что usability studies, которые необходимо проводить при разработке публичных библиотек, покажут что новичкам сложнее понимать методы работающие с ref и out параметрами, а наличие ref и out в языке будет способствовать неправильному их применению (конкретно это доказано уже много раз). Повторюсь, ref и out — не зло, но они могут мешать приведенным выше принципам разработки публичных библиотек и пользования языком в целом.
Re[4]: out- и ref-параметры.
От: SV.  
Дата: 24.02.10 13:58
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>Параметры out и ref не очень способствуют выполнению этих принципов. Я практически уверен, что usability studies, которые необходимо проводить при разработке публичных библиотек, покажут что новичкам сложнее понимать методы работающие с ref и out параметрами, а наличие ref и out в языке будет способствовать неправильному их применению (конкретно это доказано уже много раз). Повторюсь, ref и out — не зло, но они могут мешать приведенным выше принципам разработки публичных библиотек и пользования языком в целом.


Ну и что? Дай дураку хрустальный хрен — он и хрен сломает, и руки изрежет. Посмотрите на COM. Там это решено очень просто: в основе лежит близкий к корням механизм указателей, которые видны в плюсах as is, но в VB [retval] превращался в возвращаемое функцией значение (а исходный HRESULT — в тыкв... то есть, в исключение). Кесарю — кесарево, ВэБэшнику — изиотический синтаксис. Почему бы не ввести еще какой-нибудь язык, типа, Dummy.Net, в котором исходное API с рефаутами выглядело бы как без рефаутов? И овцы сыты, и волки целы.
Re[4]: out- и ref-параметры.
От: fmiracle  
Дата: 24.02.10 15:28
Оценка:
Здравствуйте, Undying, Вы писали:

U>И чем это принципиально лучше варианта с out? По мне твой вариант и вариант с out по читабельности равнозначны.


Я говорил что вариант принципиально лучше? Ты просил привести кардинально лучший вариант? Нет, просто "как это можно".

Варианты именно равнозначны, т.е. без ref/out можно, при желании, обойтись, если использовать объектно-ориентированный подход. ref/out — наследие процедурного подхода в программировании.

Я, правда, не призываю от них отказываться — к параноидальному стремлению к "чистоте подхода" не склонен. Особенно учитывая, что есть задачи, где ref/out действительно очень полезны. Но это не TryParse, а перевод на C# старого кода с процедурных языков или интеграция с нативным кодом на таких языках.


U>Но в общем случае out'ы гибче, например, если понадобиться возвращать также сообщение об ошибке, то в варианте с out будет достаточно добавить перегрузку bool TryParse(out int result, out string error), а при твоем подходе придется писать класс ConvertionResultWithError<T>.


Либо изменить сам класс, добавив к нему поле. Либо сделать наследника.
При этом, что характерно, можно оставить сигнатуту метода без изменения, и все те места, где использовалась старая функция останутся без изменения. А там где потребуется — можно будет проверить и текст тоже.

Так что вполне равнозначное решение.

U>Иногда создание дополнительного класса оправдано, конечно, но далеко не всегда.


Не вижу причин, по которым нельзя создать еще один (или не один) класс.
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Re[2]: out- и ref-параметры.
От: Mr.Cat  
Дата: 24.02.10 15:50
Оценка:
Здравствуйте, MozgC, Вы писали:
MC>BRIAN PEPIN
MC>JASON CLARK
Кто все эти люди? К их советам прямо-таки обязательно прислушиваться?
Re[2]: out- и ref-параметры.
От: Mr.Cat  
Дата: 24.02.10 15:53
Оценка:
Здравствуйте, MozgC, Вы писали:
MC>Не используйте параметры out
Кстати говоря:

Methods that implement the Try<Something> pattern, such as Int32TryParse()()(), do not fire this violation. The following example shows a structure (value type) that implements the Int32TryParse() method...

Re[4]: out- и ref-параметры.
От: Ромашка Украина  
Дата: 24.02.10 17:17
Оценка: +2
Здравствуйте, Undying:
> Ты всерьез считаешь, что код...

Я всерьез считаю, что лучше всех этих извращений будет:

int? result = int.TryParse(text);
Posted via RSDN NNTP Server 2.1 beta


Всё, что нас не убивает, ещё горько об этом пожалеет.
Re[5]: out- и ref-параметры.
От: artelk  
Дата: 24.02.10 19:50
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>А если так:

_FR>
_FR>var result = TryParse("");
_FR>if(result) {
_FR>  Func(result.Result);
_FR>}//if
_FR>


Имхо было бы здорово, если так:
var result = int.TryParse("");
if(result.HasValue)
    Func(result.Value);
Re[4]: out- и ref-параметры.
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 24.02.10 23:36
Оценка:
Здравствуйте, Undying, Вы писали:

F>>
F>>ConvertionResult<int> result = int.TryParse( "asdf" );
F>>if( !result.IsValid )
F>>{
F>>    Console.WriteLine("It was not an integer number");
F>>    return;
F>>}
F>>Console.WriteLine( result.Value );
F>>


U>И чем это принципиально лучше варианта с out?


Попробуй переписать с out такой код:
var sum =
    input
        .Select(int.TryParse)
        .Where(r => r.IsValid)
        .Sum(r => r.Value);
... << RSDN@Home 1.2.0 alpha 4 rev. 1458 on Windows 7 6.1.7600.0>>
AVK Blog
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.