Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / WinForms, .Net Framework Новый топик    Ответить
 Default interface methods (C# 8)  [new]
fkthat
Member

Откуда:
Сообщений: 4880
Default interface methods (C# 8)
Использую.
28,6%
 (2)
Не использую.
57,1%
 (4)
Говнофича и Зло.
14,3%
 (1)
Голосование открыто только для зарегистрированных пользователей.
Проголосовало: 7  

Используете ли?
26 апр 21, 00:23    [22314142]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
hVostt
Member

Откуда:
Сообщений: 19326
fkthat,

норм фича
26 апр 21, 01:52    [22314177]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
Сон Веры Павловны
Member

Откуда:
Сообщений: 6192
hVostt
fkthat,

норм фича

21962407
hVostt
ЕвгенийВ,

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

MS за прошедшие с тех пор полтора года успел что-то сильно переделать в этой фиче?
26 апр 21, 06:08    [22314190]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
fkthat
Member

Откуда:
Сообщений: 4880
hVostt,

Мне сама идея не особо как-то сначала понравилась. Но есть очень типичная ситуация, когда для удобства хочется в интерфейсе иметь больше методов, но при этом большая их часть всегда и везде будет реализовываться просто через другие методы того же интерфейса. И типовым решением для этого было использовать методы-расширения - можно посмотреть, например, как устроены интерфейсы IServiceProvider или ILogger - в них самих всего по одному методу, а все остальные "их" методы это методы-расширения. (Я сам не большой любитель расширений, но вот это, как раз случай, где расширения подходят просто идеально.) И вот, я подумал, что DIM в таких случаях могут быть удобной альтернативой:

1) Нет риска поломки из-за конфликта с "обычным" методом.
2) Нет зависимости от наличия using.
3) Можно определять свойства.

Попробовал заменять расширения дефолтными методами и сразу же наткнулся на проблемы:

1) Дефолтный метод доступен только через интерфейс, но не через класс, который этот интерфейс реализует (точь в точь как explicit method implementation). Обычно это проблем вызывать не должно, т.к. DI и все используется через интерфейсы. Но, есть, все-таки, ситуации когда это может быть проблемой. Допустим, я пишу какой-то библиотечный класс и просто не хочу закладываться на то, что тот, кто его будет использовать обязательно будет использовать его через интерфейс, а не напрямую.

2) Моки. Вот это проблема уже настоящая. Castle (по крайней мере в текущей версии) не может создавать dynamic proxy для интерфейсов с дефолтными методами. Нашел в итоге обходной путь:

public interface IFoo
{
    string Name {get;}

    string Hello => $"Hello, {Name}!";
}

// вспомогательная пустая реализация IFoo для тестов
public abstract class TestingFoo: IFoo
{
    public abstract string Name { get; }
}

[Fact]
public void Hello_ShouldReturnHelloName()
{
    // создаём мок не для самого интерфейса, а для его вспомогательной реализации
    // переменная объявляется явно как IFoo, потому что DIM доступны только при вызове через интерфейс
    IFoo foo = A.Fake<TestingFoo>();
    A.CallTo(() => foo.Name).Returns("World");
    foo.Hello.Should().Be("Hello, World!");
}


Так работает, но вся конструкция в целом несколько уродливая, все это приходится делать везде где нужен мок для IFoo, и всегда надо помнить что IFoo (и другие подобные) надо мокать не напрямую, а только через вспомогательную абстрактную реализацию.

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

И еще, с т.з. идеологии. Само по себе наличие у метода интерфейса реализации по-умолчанию это вполне нормально, почему бы и нет. Но синтаксически все это сделано как-то некрасиво. Интерфейс, по своей сути, это просто спецификация какого-то public API компонента. И смешивать её с кодом реализации смотрится как-то не очень. Это как если бы в файл OpenAPI сваггера можно было бы вписать какой-нибудь код на PHP с реализацией описанного там вызова :)
26 апр 21, 11:11    [22314306]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 22390
fkthat
1) Дефолтный метод доступен только через интерфейс, но не через класс, который этот интерфейс реализует (точь в точь как explicit method implementation). Обычно это проблем вызывать не должно, т.к. DI и все используется через интерфейсы. Но, есть, все-таки, ситуации когда это может быть проблемой. Допустим, я пишу какой-то библиотечный класс и просто не хочу закладываться на то, что тот, кто его будет использовать обязательно будет использовать его через интерфейс, а не напрямую.
А в чем тут проблема-то, я не понял. Если реализация есть, то она и будет использоваться, если нет, подтянется из интерфейса. Какая разница, через что это будет использоваться потребитель? это совсем не то же самое, что explicit method implementation
26 апр 21, 11:17    [22314315]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
fkthat
Member

Откуда:
Сообщений: 4880
Shocker.Pro
А в чем тут проблема-то, я не понял. Если реализация есть, то она и будет использоваться, если нет, подтянется из интерфейса. Какая разница, через что это будет использоваться потребитель?

Вот в чем. Возьмем интерфейс из моего примера выше и его реализацию:
public class Foo: IFoo
{
    public string Name => "World";
}

А теперь, допустим, что это часть библиотеки, которую использует кто-то другой. И такой код:
Foo foo = new();
Console.WriteLine(foo.Hello);
у него вообще не соберется. Т.е. надо будет либо указывать в доках (которые никто не читает :), что писать можно только так:
IFoo foo = new Foo();
Console.WriteLine(foo.Hello);
либо как-то вообще запретить использовать класс Foo напрямую (например, заставить создавать только через фабрику или фабричный метод, что отдает какими-то лишними костылями)

Shocker.Pro
это совсем не то же самое, что explicit method implementation

Я это знаю. Я и имел в виду не "то же самое" а "ведет себя так же в плане доступа к методу".
26 апр 21, 11:32    [22314330]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 22390
А, дошло, да, такие кейсы я не использовал... за ненадобностью )))
26 апр 21, 11:46    [22314346]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
Roman Mejtes
Member

Откуда: г. Пермь
Сообщений: 4207
получается, в C# подвезли множественное наследование "абстрактных классов"?
26 апр 21, 15:48    [22314543]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
fkthat
Member

Откуда:
Сообщений: 4880
Roman Mejtes
получается, в C# подвезли множественное наследование "абстрактных классов"?

Нет. Но это близко к "traits".
26 апр 21, 16:18    [22314565]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
ЕвгенийВ
Member

Откуда: Москва
Сообщений: 4986
fkthat,
Еще так работает.

    public interface IFoo
    {
        string Name { get; }

        string Hello => $"Hello, {Name}!";
        static void Main(string[] args)
        {
            Console.WriteLine("Привет мир");
        }
    }
26 апр 21, 17:37    [22314633]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
Где-то в степи
Member

Откуда: Под Таганрогом
Сообщений: 4370
Ну очевидно удобная штука, в каком контексте ( если судить по джаве)
вы пишете что то большое и монструозное, промышленный интерфейс с кучей методов, ну типа орм.
Проходит время нужно добавить ряд методов, вы добавляете в интерфейс и бац. компиляция не проходит, просит их реализовать в наследниках.
рутина (((( ты начинаешь рвать волосы еююй интерфейс обыкновенный класс с виртуалами ума хватило у разрабов.
а тут бац default по месту обявления и в шеколаде. ( я думаю они так и сперли)
26 апр 21, 19:06    [22314696]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
ЕвгенийВ
Member

Откуда: Москва
Сообщений: 4986
Где-то в степи
( я думаю они так и сперли)

это жаба обычно спирает, посмотрите на атрибуты или LINQ
тут задел на будущее, в ближайших версиях .NET станет возможна прямая с жабой, последняя будет подключаться как либа с возможностью использовать все как есть, можно например отнаследоваться от жаба класса будет
26 апр 21, 20:33    [22314720]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
Где-то в степи
Member

Откуда: Под Таганрогом
Сообщений: 4370
ЕвгенийВ,
скрестить ежа с жабой, скрепьненько )) тырят труг у друга помаленьку, а вот деревья выражений слабовато, а была бы революция в языке. скучноватый язык ( хоть и пишу на нем последние 5 лет - не мало)
26 апр 21, 21:17    [22314747]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
Shocker.Pro
Member

Откуда: ->|<- :адуктО
Сообщений: 22390
Roman Mejtes
получается, в C# подвезли множественное наследование "абстрактных классов"?
Нет, потому что у интерфейса нет состояния
26 апр 21, 21:55    [22314759]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
fkthat
Member

Откуда:
Сообщений: 4880
Где-то в степи
вы пишете что то большое и монструозное, промышленный интерфейс с кучей методов, ну типа орм. Проходит время нужно добавить ряд методов, вы добавляете в интерфейс и бац. компиляция не проходит, просит их реализовать в наследниках.

Если все это свой собственный проект, то:

1) Это врят ли особая проблема, т.к. сомнительно, что в пределах одного проекта будет хотя бы полдюжины разных реализаций одного интерфейса.

2) Если же мы решаем проблему из п.1 через DIM, то мы сразу же переносим её на юнит-тесты, в которых интерфейс мокается (как я писал выше). Все юнит-тесты которые мокают этот интерфейс сразу же поломаются.

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

Сценарий, например, такой:

1) Вася создал мегафреймворк "Foo" в котором есть интерфейс IFoo для его расширения и публикует его как NuGet пакет Foo с версией 1.0.0.

2) Петя цепляет версию Foo.1.0.0 в свою либу Bar, реализует в ней свой компонент-расширение с интерфейсом IFoo, и публикует свое творение как Bar.1.0.0.

3) Васин фреймворк очень успешен, у него куча идей по его развитию, и он добавляет к своему интерфейсу IFoo еще один метод. Вася успешный инвестор опытный разработчик соблюдающий заветы SemVer, поэтому, зная, что изменение интерфейса может поломать совместимость, увеличивает мажорную версию, и свое новое произведение публикует уже как Foo.2.0.0. У Пети с этим проблем нет, потому что его пакет зависит от версии Foo.1.0.0 где интерфейс еще прежний.

4) А вот теперь мы в своем проекте цепляем одновременно либу Пети Bar.1.0.0 и фреймворк Васи Foo.2.0.0 (мы же хотим быть на острие прогресса и берем все самое последнее). И вот как раз тогда у нас все действительно падает.

Но такой сценарий, все-таки, не отнесешь к тому, с чем каждый день сталкиваешься.
26 апр 21, 22:28    [22314781]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
fkthat
Member

Откуда:
Сообщений: 4880
Shocker.Pro
Нет, потому что у интерфейса нет состояния

fkthat
Нет. Но это близко к "traits".
26 апр 21, 22:29    [22314782]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
hVostt
Member

Откуда:
Сообщений: 19326
Сон Веры Павловны
hVostt
fkthat,

норм фича

21962407
hVostt
ЕвгенийВ,

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

MS за прошедшие с тех пор полтора года успел что-то сильно переделать в этой фиче?


Неожиданно нашли ей применение при рефакторинге и особенно в библиотечных компонентах.
26 апр 21, 22:56    [22314794]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
hVostt
Member

Откуда:
Сообщений: 19326
fkthat
1) Дефолтный метод доступен только через интерфейс, но не через класс, который этот интерфейс реализует (точь в точь как explicit method implementation). Обычно это проблем вызывать не должно, т.к. DI и все используется через интерфейсы. Но, есть, все-таки, ситуации когда это может быть проблемой. Допустим, я пишу какой-то библиотечный класс и просто не хочу закладываться на то, что тот, кто его будет использовать обязательно будет использовать его через интерфейс, а не напрямую.


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

Чем это лучше расширений? Тем, что дефолтные методы интерфейса можно имплементировать, а расширения -- нет.

fkthat
2) Моки. Вот это проблема уже настоящая. Castle (по крайней мере в текущей версии) не может создавать dynamic proxy для интерфейсов с дефолтными методами. Нашел в итоге обходной путь:


Дефолтные методы как раз мокаются на ура. Насчёт динамик прокси, если тебе это нужно, то скорее всего ты используешь их неправильно.

Это как методы расширения, только в ряде случаев лучше. Сложной логики там быть не должно. Обычно рутинное приведение, или вариации для удобного использования. Никакой логики.
26 апр 21, 23:01    [22314799]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
Где-то в степи
Member

Откуда: Под Таганрогом
Сообщений: 4370
fkthat,
судя по изложенному, этот проблема называется diamond, не проверял, но имхо компилятор пошлет вас нах, и будет требовать
явный вызов, и вообще это сразу оговривалось при презентации этих фитч, и статус у них - компромисный в контесте оп.
тоже самое и статик..
26 апр 21, 23:02    [22314801]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
fkthat
Member

Откуда:
Сообщений: 4880
hVostt
Дефолтные методы как раз мокаются на ура.

Нет. На практике проверено. И проблема упирается как раз в Кастел (который все мок-фреймворки используют). В крайней версии Moq какой-то workaround уже встроили, но, вот, в FakeItEasy (кторым я как раз и пользуюсь) этого еще нет - по обсуждению в issues их позиция - "подождем, пока это будет поддерживаться нативно в Castle".

Я, в общем, подумал тут, что может быть форкну на выходных FakeItEasy и заплатку поставлю, чтобы он с DIM работал - у меня идея уже есть как это сделать.

Впрочем, ты, м.б. о том, что руками они "мокаются на ура" - но ведь это же совсем колхоз-совхоз :))
26 апр 21, 23:35    [22314809]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
fkthat
Member

Откуда:
Сообщений: 4880
hVostt
Это как методы расширения, только в ряде случаев лучше.

Да, я об этом и написал, но, как видишь, там свои проблемы при этом возникают.

hVostt
Насчёт динамик прокси, если тебе это нужно, то скорее всего ты используешь их неправильно.

Я их не использую сам, но их используют мок-фреймворки. Проблема с ними. Вот такой код сразу же кидает ексепшен:
public interface IFoo
{
    string Name {get;}

    string Hello => $"Hello, {Name}!";
}

[Fact]
public void Hello_ShouldReturnHelloName()
{
    var foo = A.Fake<IFoo>();
    A.CallTo(() => foo.Name).Returns("World");
    foo.Hello.Should().Be("Hello, World!");
}

Как обойти я уже в самом начале писал, но мне такой способ не по душе - не очень как-то красиво выглядит.
26 апр 21, 23:40    [22314812]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
fkthat
Member

Откуда:
Сообщений: 4880
hVostt,

Что обидно, с Moq моки работают. Вот это вот вполне ок:
[Fact]
public void Hello_ShouldReturnHelloName()
{
    var fooMock = new Mock<IFoo>();
    fooMock.Setup(m => m.Name).Returns("World");
    fooMock.Setup(m => m.Hello).CallBase();
    fooMock.Object.Hello.Should().Be("Hello, World!");
}

Но, я, вот, Moq уже разлюбил и полюбил вместо него FakeItEasy за намного более компактный синтаксис.
26 апр 21, 23:58    [22314818]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
hVostt
Member

Откуда:
Сообщений: 19326
fkthat
Впрочем, ты, м.б. о том, что руками они "мокаются на ура" - но ведь это же совсем колхоз-совхоз :))


Эм.. ну погоди. Дефолтные методы как раз мокать не обязательно, ты мокаются те методы, которые они используют. Т.е. тут трагической проблемы нет :)

Давай ситуёвину представим. Ты мокаешь свой интерфейс, а компонент юзает интерфейс через экстеншен методы. Пральна? Ты ж не можешь это замокать. То на то и выходит.

Но можно и руками. В любом случае возможностей больше, чем с экстеншенами :)
28 апр 21, 22:40    [22316008]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
hVostt
Member

Откуда:
Сообщений: 19326
fkthat
Я их не использую сам, но их используют мок-фреймворки. Проблема с ними. Вот такой код сразу же кидает ексепшен:


аа... ну да, проблема понятна. придётся ждать, когда подтянутся.
28 апр 21, 22:41    [22316010]     Ответить | Цитировать Сообщить модератору
 Re: Default interface methods (C# 8)  [new]
hVostt
Member

Откуда:
Сообщений: 19326
fkthat
Но, я, вот, Moq уже разлюбил и полюбил вместо него FakeItEasy за намного более компактный синтаксис.


по мне так однофигственно :)
28 апр 21, 22:42    [22316011]     Ответить | Цитировать Сообщить модератору
Все форумы / WinForms, .Net Framework Ответить