Re[19]: Не понимая и половины
От: vdimas Россия  
Дата: 22.03.17 08:26
Оценка:
Здравствуйте, samius, Вы писали:

S>Никакая джит оптимизация цикла в этом конкретном Sum не может отменить тот факт, что цикл будет дергать MoveNext/Current через интерфейс.


Для вэлью типов не факт, если в теле генерик метода обычный foreach.
Re[20]: Не понимая и половины
От: samius Япония http://sams-tricks.blogspot.com
Дата: 22.03.17 09:16
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, samius, Вы писали:


S>>Никакая джит оптимизация цикла в этом конкретном Sum не может отменить тот факт, что цикл будет дергать MoveNext/Current через интерфейс.


V>Для вэлью типов не факт, если в теле генерик метода обычный foreach.


Так продемонстрируй сей "не факт", раз по-твоему джит способен подменять типы локальных переменных и вызываемые методы. Это очень серьезный "не факт". Я полагаю что ему место в багтрэкере.
Re[21]: Не понимая и половины
От: vdimas Россия  
Дата: 22.03.17 10:49
Оценка:
Здравствуйте, samius, Вы писали:

V>>Для вэлью типов не факт, если в теле генерик метода обычный foreach.

S>Так продемонстрируй сей "не факт", раз по-твоему джит способен подменять типы локальных переменных и вызываемые методы. Это очень серьезный "не факт". Я полагаю что ему место в багтрэкере.

А почему только я что-то демонстрирую?
Почему бы тебе не продемонстрировать, что там порождает джит?
Re[16]: Минутка WTF-19: Catch me if you can
От: Sinix  
Дата: 22.03.17 14:45
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Почти так. Дело не только в массиве.

V>Если объекты агрегируют друг друга, то в плюсах это обычно происходит естественным образом — по значению.
V>Отсюда "бесплатность" абстракций.
+1 тогда.
Увы, к сегодняшнему дотнету это явно не относится. Не, _принципиальных_ проблем исправить очень многие вещи нет, но существенных инвестиций в перфоманс от команды .core пока не предвидится — им и без того работы хватает. Ну а "взрослый" фреймворк на сегодня полузаброшен, все ушли на фронт .net core.

S>>Но это справедливо именно для тшательно вылызанного perf-critical кода. Для всего остального никто с тонной вложенных структур заморачиваться не будет, возвращаемся к результатам выше



V>Ну да. Считанные единицы объектов верхнего уровня могут быть не заморочены. ))


Скорее, наоборот. По крайней мере, для энтерпрайза. Когда бизнес-логика занимает 5-10% общего времени, а всё остальное io|запросы|orm, никто инвестировать в поголовную оптимизацию всего кода не будет. Наоборот, отдельные овероптимизации, бывает, переписываются на более простой и поддерживаемый код. -15k строк в обмен на +5-10 лишних мс к запросу? Как по мне — чистый win.
Re[22]: Не понимая и половины
От: samius Япония http://sams-tricks.blogspot.com
Дата: 22.03.17 15:25
Оценка: -1
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, samius, Вы писали:


V>>>Для вэлью типов не факт, если в теле генерик метода обычный foreach.

S>>Так продемонстрируй сей "не факт", раз по-твоему джит способен подменять типы локальных переменных и вызываемые методы. Это очень серьезный "не факт". Я полагаю что ему место в багтрэкере.

V>А почему только я что-то демонстрирую?

V>Почему бы тебе не продемонстрировать, что там порождает джит?

Дык я ж могу продемонстрировать, но только обратное. Т.е. то, что цикл foreach в теле генерик метода, параметризованного типом T с констрейнтом IEnumerablt<int> производит вызов именно метода, реализующего IEnumerable<int>.GetEnumerator() и возвращающего ссылочный IEnumerator<int>. Метод, возвращающий value-enumerator при этом не вызывается. Тут даже не надо смотреть, что именно порождает джит, т.к. все это видно по побочным эффектам.
Но ведь именно ты заявляешь, что это не факт? И при этом не приводишь аргументы, на чем основан твой "не факт". Ни примера, ни документации. Т.е. никаких оснований полагать что твоя оптимизация на что-либо способна, кроме того что бы в пределе n->(U+221E) избавиться от 1-го боксинга. Если с этим согласен, то не надо ничего демонстрировать. И так все понятно.
Re[20]: Не понимая и половины
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 22.03.17 15:46
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, samius, Вы писали:


S>>Никакая джит оптимизация цикла в этом конкретном Sum не может отменить тот факт, что цикл будет дергать MoveNext/Current через интерфейс.


V>Для вэлью типов не факт, если в теле генерик метода обычный foreach.

Там заранее известен тип к чему он обращается. Для массивов никаких MoveNext/Current.
Для Linq операторов может оптимизирован до roslyn-linq-rewrite


Но вот тебе и C++ не с оптимизирует если на вход подается пользовательская функция с IEnumerable вместо массива.
Просто итераторы в С++ по другому устроены.
Кстати а вот кстати с появлением ref return

public static ref int Find3(int[,] matrix, Func<int, bool> predicate)
{
    for (int i = 0; i < matrix.GetLength(0); i++)
        for (int j = 0; j < matrix.GetLength(1); j++)
            if (predicate(matrix[i, j]))
                return ref matrix[i, j];
    throw new InvalidOperationException("Not found");
}

Now that the method returns a reference to the integer value in the matrix, you need to modify where it's called. The var declaration means that valItem is now an int rather than a tuple:

var valItem = MatrixSearch.Find3(matrix, (val) => val == 42);
Console.WriteLine(valItem);
valItem = 24;
Console.WriteLine(matrix[4, 2]);


Можно и на аналог С++ итераторов замахнуться. Только это особо не нужно.
и солнце б утром не вставало, когда бы не было меня
Re[19]: Не понимая и половины
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 24.03.17 08:44
Оценка:
Здравствуйте, samius, Вы писали:


S>Причем тут вообще эта таблица и джит? Я пытаюсь выяснить у тебя, за счет чего ты ожидаешь уменьшение косвенности в методах типа

S>
S>int Sum<T>(T coll) where T : IEnumerable<int>
S>

S>Никакая джит оптимизация цикла в этом конкретном Sum не может отменить тот факт, что цикл будет дергать MoveNext/Current через интерфейс. Так чем же он лучше, чем обычный
S>
S>int Sum(IEnumerable<int>)
S>


Кстати интересная статья
C# value type boxing under the hood

interface IAdd
{
    void Add(int val);
}

struct Foo : IAdd
{
    public int value;

    void IAdd.Add(int val)
    {
        value += val;
    }

    public void AddValue(int val)
    {
        value += val;
    }

    public void Print(string msg)
    {
        Console.WriteLine(msg + ":" + value);
    }
}




Adding constraints
Now the interesting case that I’d like to talk about earlier in the article (thanks for staying with me so far!). Let’s create a generic method with a generic constraint where the T is an IAdd interface:

 static void Add_WithConstraints<T>(ref T foo, int val) where T : IAdd
    {
        foo.Add(val);
    }

    Add_WithConstraints<Foo>(ref f, 10);
    f.Print("After Add_WithConstraints");

Perhaps it isn’t entirely obvious to everyone — foo.Add(val) is an interface call using callvirt instruction: callvirt instance void IAdd::Add(int32), because that’s the only way compiler knows how to make the call.
The interesting part is, when we call Add_WithConstraints, the call happens exactly in the same manner, except the code we are calling into looks drastically different:
0:000> !u 00007ff8`cfa707d0
Normal JIT generated code

Program.Add_WithConstraints[[Foo, value]](Foo ByRef, Int32)
Begin 00007ff8cfa707d0, size 3a
>>> push    rbp
sub     rsp,20h
lea     rbp,[rsp+20h]
mov     qword ptr [rbp+10h],rcx           ; this pointer
mov     dword ptr [rbp+18h],edx           ; val
mov rax,7FF8CF964560h                     ; debugger gibberish 
                                          ; but you probably guessed it's for Just My Code
cmp     dword ptr [rax],0
je      00007ff8`cfa707f5
call    clr!JIT_DbgIsJustMyCode (00007ff9`2f534eb0)
nop
mov     rcx,qword ptr [rbp+10h]                  ; this pointer
mov     edx,dword ptr [rbp+18h]                  ; val
call    00007ff8`cfa706c0 (Foo.IAdd.Add(Int32)   ; calls the method without boxing!
nop
nop
lea     rsp,[rbp]
pop     rbp
ret

As you can see, the code is surprisingly simple. No boxing, no interface cast, and a direct call to Foo.IAdd.Add method. No value is lost. And you can observe the side effect:
After Add_WithConstraints:30
The reason is compiler now has enough information to figure out the code is for Foo and the interface call will land exactly on Foo.IAdd.Add, so it skips the formality and calls the function directly. This is both a performance optimization but also comes with observable side-effect.
Conclusion
When you are working with interface on value types, be aware of the potential performance cost of boxing and correctness problem of not observing changes in the callee. If you’d like to avoid that, you can use generic constraints to constraint the interface call so that compiler can optimize out the boxing and interface call altogether and go straight to the right function.
You can find the full code in this gist.

и солнце б утром не вставало, когда бы не было меня
Re[14]: Junk in — junk out
От: Qbit86 Кипр
Дата: 24.03.17 09:38
Оценка:
> Ты опять мне пытаешься доказать, что такие алгоритмы, корректно работающие для вэлью и реф типов, всё-таки, есть?

Ты же писал про «поиски классов таких алгоритмов, которые, таки, работают в обоих случаях»? Вот я тебе такой класс предоставляю.

> Я могу в прикладном виде описать гарантии, которые не дадут мне привести ref-объект в невалидное состояние.

> Для value-типов ср-в обеспечения таких гарантий нет.

Это всё находится снаружи обобщённой библиотеки; на её код не влияет, сможешь ли ты вне библиотеки выразить те или иные гарантии. Если бы язык позволял выразить требование non-zero-initialized переданных аргументов, функция `Sum` всё равно не ограничивала бы свои аргументы этим требованием, потому что в общем случае передача zero-initialized-объекта того же `TMonoid` — just fine. Конечно, автор может реализовать констрейнты конкретными структурами так, что zero-initialized окажется невалидным. Но библиотека не должна отвергать все zero-initialized экземпляры на этом основании.

> Собсно, моё разочарование в дотнете больше было не от его тормознуточти, а от непоследовательной системы типов


Поэтому ты вернулся в «лоно C++», где получить невалидный объект не то что с нулями, а просто с рандомным мусором — проще простого? Независимо от того, что там у него за конструкторы пытаются обеспечить какие-то там гарантии.
Глаза у меня добрые, но рубашка — смирительная!
Re[15]: Junk in — junk out
От: vdimas Россия  
Дата: 24.03.17 11:08
Оценка:
Здравствуйте, Qbit86, Вы писали:

>> Ты опять мне пытаешься доказать, что такие алгоритмы, корректно работающие для вэлью и реф типов, всё-таки, есть?

Q>Ты же писал про «поиски классов таких алгоритмов, которые, таки, работают в обоих случаях»? Вот я тебе такой класс предоставляю.

Это надо так понимать, что упорно продолжаешь делать попытка переформулировать моё исходное утверждение?
Хорошо себя чувствуешь?


Q>Это всё находится снаружи обобщённой библиотеки; на её код не влияет, сможешь ли ты вне библиотеки выразить те или иные гарантии.


И опять ты утверждаешь нечто, что даже хуже самых банальных банальностей.
Разумеется, каждый отдельно взятый сниппет кода может верен.
Но бы обсуждаем ту засаду, которая приключилась при совместной работе разных участков кода.


Q>Поэтому ты вернулся в «лоно C++», где получить невалидный объект не то что с нулями, а просто с рандомным мусором — проще простого?


1. чем нули отличаются от рандомного мусора?
2. получить невалидный объект в С++ не просто — для этого надо "кое-что" сделать, например, специально реинтерпретировать память.

Т.е., опять банальная банальность в том, что речь идёт о неявных вещах. Проблему в C# я получаю неявно, а в С++ — только явно.
Прочувствуйте разницу, как грится.


Q>Независимо от того, что там у него за конструкторы пытаются обеспечить какие-то там гарантии.


Это ты лишь показываешь невладение языком.
Re[16]: Владелец языка
От: Qbit86 Кипр
Дата: 24.03.17 13:19
Оценка: -1
Здравствуйте, vdimas, Вы писали:

V>Это надо так понимать, что упорно продолжаешь делать попытка переформулировать моё исходное утверждение? :)))


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

V>Хорошо себя чувствуешь?


Сонливо.

Q>>Поэтому ты вернулся в «лоно C++», где получить невалидный объект не то что с нулями, а просто с рандомным мусором — проще простого?


V>1. чем нули отличаются от рандомного мусора?


Нули предсказуемы и ожидаемы. Это ни undefined behavior, ни unspecified behavior.

V>2. получить невалидный объект в С++ не просто — для этого надо "кое-что" сделать, например, специально реинтерпретировать память.


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

V>Это ты лишь показываешь невладение языком.


Это мы уже слышали. Давай лучше расскажи нам, насколько ты оцениваешь своё владение языком по шкале от одного до десяти.
Глаза у меня добрые, но рубашка — смирительная!
Re[20]: Не понимая и половины
От: samius Япония http://sams-tricks.blogspot.com
Дата: 24.03.17 18:26
Оценка: :)
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, samius, Вы писали:


S>>Никакая джит оптимизация цикла в этом конкретном Sum не может отменить тот факт, что цикл будет дергать MoveNext/Current через интерфейс. Так чем же он лучше, чем обычный

S>>
S>>int Sum(IEnumerable<int>)
S>>


S> Кстати интересная статья

S>C# value type boxing under the hood

Если эту статью переносить на случай "int Sum<T>(T coll) where T :IEnumerable<int>", то статья нам указывает на то, что метод IEnumerable<T>.GetEnumerator() будет вызван действительно без боксинга. Однако, результат этого метода IEnumerator<T>. Интерфейс, не структура. И методы интерфейса IEnumerator<T> будут далее вызываться как методы интерфейса. Ведь что бы получить value-перечислитель, нужно вызвать метод
MyValueEnumerator GetEnumerator()
,а этот метод не удовлетворяет сигнатуре
IEnumarator<T> GetEnumerator()
Т.е. совсем другой метод. Компилятор или джит (не суть важно) могут вызывать лишь метод, на который им указывает ограничение T. Других методов-то они вообще не видят так, как их видят шаблоны в C++. В итоге, компилятор или джит могут вызвать этот метод но как-то по-другому, т.е. не через интерфейс, а напрямую. Они не могут вызвать метод, который не обещан ограничением. И не могут изменить тип результата с IEnumerator<T> на MyValueEnumerator. В итоге лишь один вызов GetEnumerator() может быть вызван не через интерфейс. Все остальные методы при foreach забеге будут вызваны через IEnumerator<T>.
Re[21]: Не понимая и половины
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 24.03.17 19:57
Оценка:
Здравствуйте, samius, Вы писали:


Я всего на всего привел интересный прием для вызова без боксинга.
Только и всего.
и солнце б утром не вставало, когда бы не было меня
Re[17]: Владелец языка
От: vdimas Россия  
Дата: 24.03.17 21:41
Оценка:
Здравствуйте, Qbit86, Вы писали:

V>>Это надо так понимать, что упорно продолжаешь делать попытка переформулировать моё исходное утверждение?

Q>Это называется не переформулировать, а процитировать. Это ведь ты его вытащил в эту ветку, никто тебя за язык не тянул.

Ну так процитируй и устыдись.
Re[21]: Не понимая и половины
От: vdimas Россия  
Дата: 24.03.17 23:55
Оценка:
Здравствуйте, samius, Вы писали:

S>Если эту статью переносить на случай "int Sum<T>(T coll) where T :IEnumerable<int>", то статья нам указывает на то, что метод IEnumerable<T>.GetEnumerator() будет вызван действительно без боксинга. Однако, результат этого метода IEnumerator<T>.


Может и так, а может и нет.
Ты уже присоединился отладчиком в релизному бинарнику и проверил?

Там же происходит парная операция:
— боксинг структуры, реализующей IEnumerator;
— обращение к полям этой структуры.

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

Я просто хочу, чтобы ты проверил — будет ли там вызов метода интерфейса или прямой.


S>Интерфейс, не структура.


Это в терминах опкодов VM там интерфейс, что не интресует от слова совсем.
Напомню, что вэлью типов джит генерит уникальную версию метода под каждый такой тип.


S>И методы интерфейса IEnumerator<T> будут далее вызываться как методы интерфейса.


Не надо ничего объяснять, как оно там в опкодах это и так понятно. ))


S>Т.е. совсем другой метод. Компилятор или джит (не суть важно) могут вызывать лишь метод, на который им указывает ограничение T. Других методов-то они вообще не видят так, как их видят шаблоны в C++.


Как раз для вэлью типов это не совсем так. Интерфейсный метод в исходном вэльютипе может вернуть другой вэльютип, реализующий IEnumerator<>. Джит это прекрасно видит, т.е. такие оптимизации принципиально возможны. Мне лишь любопытно — они по-факту уже происходят или еще нет? Или мне опять смотреть ассемблерный листинг? ))


S>Они не могут вызвать метод, который не обещан ограничением. И не могут изменить тип результата с IEnumerator<T> на MyValueEnumerator.


Могут аж бегом. Именно теоретически есть такая возможность, т.е. у джита есть вся необходимая для этого информация.
Ну ОК, если тебе не интересно проверить, будем считать, что это не принципиально.
Re[22]: Не понимая и половины
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.03.17 10:54
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, samius, Вы писали:



S> Я всего на всего привел интересный прием для вызова без боксинга.

S>Только и всего.

Мне кажется что он был интересным во время выхода C# 2, т.е. году в 2005-м.
Re[22]: Не понимая и половины
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.03.17 11:20
Оценка: +1 -1
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, samius, Вы писали:


S>>Если эту статью переносить на случай "int Sum<T>(T coll) where T :IEnumerable<int>", то статья нам указывает на то, что метод IEnumerable<T>.GetEnumerator() будет вызван действительно без боксинга. Однако, результат этого метода IEnumerator<T>.


V>Может и так, а может и нет.

V>Ты уже присоединился отладчиком в релизному бинарнику и проверил?

V>Там же происходит парная операция:

V>- боксинг структуры, реализующей IEnumerator;
V>- обращение к полям этой структуры.

Я уж не знаю, как тебе объяснить, что бы ты понял. Может, действительно отладчик по релизному бинарнику в этом поможет? Не уверен, попробую болдом донести.
При твоем способе вызова нет никакой структуры, реализуюющей IEnumerator. foreach не видит метод, возвращающий структуру. Он может оперировать лишь методом, который указан в ограничении. А тот возвращает класс, реализующий интерфейс.

V>В тесте выше я вижу, что если компилятор видит отсутствие обращение к экземпляру боксированного объекта со стороны третьих лиц, то это боксирование "уничтожается". Как раз если само боксирование происходит "на глазах" джит — это тот же самый случай.

Это ты мог видеть в тестах, где foreach оперирует явно методом GetEnumerator(), возвращающим структуру. Так он может делать с типом коллекции, который видно в момент компиляции. В случае передачи типа коллекции через генерик параметр компилятор видит лишь метод, указанный в ограничении. А этот метод возвращает интерфейс.

V>Я просто хочу, чтобы ты проверил — будет ли там вызов метода интерфейса или прямой.

А я хочу, что бы ты понял. В случае вызова метода GetEnumerator() структуры-параметра call, будет прямой метод. И этот прямой метод возвращает не структуру, а объект-перечислитель. Структура-перечислитель даже не создастся. И это проверено.
Структура-перечислитель может создаться в таком случае лишь в том случае, если она будет возвращена методом, возвращающим IEnumerator<T>. Но она уже будет забокшенная и unbox в этом случае делать никто не будет.


S>>Интерфейс, не структура.


V>Это в терминах опкодов VM там интерфейс, что не интресует от слова совсем.

V>Напомню, что вэлью типов джит генерит уникальную версию метода под каждый такой тип.
Уникальная версия метода под каждый тип вызовет кошерно метод IEnumerator<T> GetEnumerator() и далее будет работать универсально с возвращенным перечислителем (через интерфейс).


S>>И методы интерфейса IEnumerator<T> будут далее вызываться как методы интерфейса.


V>Не надо ничего объяснять, как оно там в опкодах это и так понятно. ))

Если не надо объяснять, то что за у тебя романтические фантазии?


S>>Т.е. совсем другой метод. Компилятор или джит (не суть важно) могут вызывать лишь метод, на который им указывает ограничение T. Других методов-то они вообще не видят так, как их видят шаблоны в C++.


V>Как раз для вэлью типов это не совсем так. Интерфейсный метод в исходном вэльютипе может вернуть другой вэльютип, реализующий IEnumerator<>. Джит это прекрасно видит, т.е. такие оптимизации принципиально возможны. Мне лишь любопытно — они по-факту уже происходят или еще нет? Или мне опять смотреть ассемблерный листинг? ))

Если бы джит видел, какой объект вернется ему за интерфейсом (в чем я очень сомневаюсь), то он то он увидел бы EnumeratorObject.


S>>Они не могут вызвать метод, который не обещан ограничением. И не могут изменить тип результата с IEnumerator<T> на MyValueEnumerator.


V>Могут аж бегом. Именно теоретически есть такая возможность, т.е. у джита есть вся необходимая для этого информация.

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

V>Ну ОК, если тебе не интересно проверить, будем считать, что это не принципиально.

Я проверил. Метод GetEnumerator(), возвращающий структуру не вызывается внутри Sum(T coll) от слова "вообще".
Re[23]: Не понимая и половины
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 25.03.17 11:51
Оценка:
Здравствуйте, samius, Вы писали:

S>Здравствуйте, Serginio1, Вы писали:


S>>Здравствуйте, samius, Вы писали:



S>> Я всего на всего привел интересный прием для вызова без боксинга.

S>>Только и всего.

S>Мне кажется что он был интересным во время выхода C# 2, т.е. году в 2005-м.

ну вообще то статья C# value type boxing under the hood

C#, boxing, and typesystem | Mar 19, 2017

Ты её читал?
и солнце б утром не вставало, когда бы не было меня
Re[24]: Не понимая и половины
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.03.17 12:00
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, samius, Вы писали:


S>>Мне кажется что он был интересным во время выхода C# 2, т.е. году в 2005-м.

S> ну вообще то статья C# value type boxing under the hood

S>C#, boxing, and typesystem | Mar 19, 2017


S>Ты её читал?

Теперь прочитал. Так что в ней нового, по твоему с 2005-го года?
Re[25]: Не понимая и половины
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 25.03.17 12:15
Оценка:
Здравствуйте, samius, Вы писали:

S>>>Мне кажется что он был интересным во время выхода C# 2, т.е. году в 2005-м.

S>> ну вообще то статья C# value type boxing under the hood

S>>C#, boxing, and typesystem | Mar 19, 2017


S>>Ты её читал?

S>Теперь прочитал. Так что в ней нового, по твоему с 2005-го года?

То что это не дженерик интерфейс

interface IAdd
{
    void Add(int val);
}
и солнце б утром не вставало, когда бы не было меня
Re[26]: Не понимая и половины
От: samius Япония http://sams-tricks.blogspot.com
Дата: 25.03.17 12:25
Оценка:
Здравствуйте, Serginio1, Вы писали:

S>Здравствуйте, samius, Вы писали:


S>>>C#, boxing, and typesystem | Mar 19, 2017


S>>>Ты её читал?

S>>Теперь прочитал. Так что в ней нового, по твоему с 2005-го года?

S>То что это не дженерик интерфейс


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

Так что нового в этой статье-то?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.