Pull to refresh
0
0
Johan @Johan

User

Send message

В целом, такая функция в 5 строк пишется. Но, как-то, это unpythonic, нет единственного способа сделать это правильно.

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

А так же, и для остальных двух:


>>> {'a', 'b'} - {'b', 'c'}  # difference
{'a'}
>>> {'a', 'b'} | {'b', 'c'}  # union
{'b', 'c', 'a'}

Вот, кстати, да. Такой подход, заодно, позволит качественно отревьюить изменения после каждого включённого правила.

Можно устанавливать используя CocoaPods. Автоматизация, и, к тому же, контроль над версией.

Тут возвращаемое значение метода Select вычисляется благодаря параметру типа Func<T, Tuple<string, int>. Я же говорю о следующей ситуации:


public interface IMessage
{
    string Text { get; set; }
}

public static class MessageFactory
{
    static T Create<T>(string message) where T: IMessage, new()
    {
        var result = T();
        result.Message = message;
        return result;
    }
}

Для вызова метода Create необходимо явно указать тип в самом вызове:


var message1 = MessageFactory.Create<SimpleMessage>("Hello world")
SimpleMessage message2 = MessageFactory.Create<SimpleMessage>("Hello world again")

Swift, в свою очередь, позволяет сделать следующее:


protocol Message {
    init()
    var text: String { get set }
}

class MessageFactory {
    static func create<T: Message>(text: String) -> T {
        var result = T()
        result.text = text
        return result
    }
}

let message: SimpleMessage = MessageFactory.create(text: "Hello world")

Как видите, тип не фигурирует в вызове, только в объявлении переменной. То есть, в данном случае, компилятор не тип выражения выводит исходя из того, что возвращает функция, а тип возвращаемого значения выводит из того, в каком выражении участвует вызов.

Имеется ввиду не тип возращаемого значения делегата, а тип возвращаемого значения самого метода наример. Если написать метод с дженерик возвращаемым типом и без параметров, то в C# при вызове прийдется явно указывать этот тип, даже при присваивании результа в переменную с конкретным типом. Swift же умеет выводить такие типы. Сюдаже, кстати, можно отнести перегрузки по возвращаемым типам, которы тоже встречаются нечасто, как правило, перегрузки работают только по параметрам.
Мои наблюдения, не подтвержденные никакими исследованиями, говорят о том, что проблемой является вывод типов. Причём, дело не только в выводе дженериков по возвращаемому значению (как я понимаю, процесс трудоемкий, потому эта фича в «классических» языках отсутствует), но даже в числах, ибо числовые литералы в Swift не имеют типа, тип определяется исходя из контекста (такого в «классических» языках тоже нет). Как-то так выглядит проблема с моего дивана.

Простой пример не особо полезного возвращаемого значения (один из методов массива):


public mutating func removeAtIndex(index: Int) -> Element

@discardableResult mutating func remove(at index: Int) -> Element

Иногда нужен только побочный эффект, а иногда и результат.


дать возможность подавлять предупреждение в вызывающем коде

А вот этого, видимо, хотели избежать. Тот кто проектирует API знает, есть ли побочный эффект у функции и может правильно аннотировать. Если побочный эффект у функции имеется, то её вызов имеет смысл даже без использования результата. Если побочного эффекта нет, то без результата вызов смысла не имеет.

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

Если у вас голый IntPtr, то его нужно освободить в Dispose(),

Освободить в финализаторе, а в Dispose() освободить и вызвать GC.SuppressFinalize(this), поскольку ресурс уже освобождён и финализация не требуется. Таким нехитрым образом мы обеспечиваем быструю сборку объекта без очереди на финализацию.


При работе со Stream финализатор, разумеется, вообще не нужен.

Классам, не владеющим неуправляемыми ресурсами, и IDisposable как правило не нужен

Это не совсем верно, IDisposable нужен, в том числе, и если вы владеете управляемыми ресурсами реализующими IDisposable. Например Stream.


Финализатор, в свою очередь, нужен в случае, если вы работаете напрямую с IntPtr.

Ну, опыт у всех разный, тут уж ничего не попишешь. Отсюда, собственно, и разнообразие подходов, и споры об их достоинствах.

В моём понимании, тут нужно двигаться от понятия "неверная абстракция". Как правило, с большей или меньшей вероятностью, можно предположить как будет развиваться код: будет он расходится или будет изменятся синхронно. В первом случае, как нетрудно догадаться, делать абстракцию будет излишним, во втором, напротив, необходимо её сделать.


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


Объясняется это тем, что ввести абстракцию будет дешевле, чем убрать ту, которая не заработала.


По крайней мере, я понял текст таким образом, и это, более или менее, согласуется с моим опытом.

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


Основной посыл статьи такойже как в The Wrong Abstraction — Sandi Metz:


duplication is far cheaper than the wrong abstraction
Я сообразил уже, спасибо.
Да, действительно, минус rethrow.
Фильтрация тут, вообще, экономия на паре скобок.
Ну, если быть до конца последовательным, то нельзя сказать что-то наверняка даже имея кусок:
Console.WriteLine("Hello Habr!");

Только так:
global::System.Console.WriteLine("Hello Habr!");

Information

Rating
Does not participate
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Date of birth
Registered
Activity