Здравствуйте, dmitry_npi, Вы писали:
_>В википедии, в статье, посвященной обобщенному програмированию, как пример, приведен кусочек кода на C#:
_>Сам по себе смысл ясен, но непонятно, чем это лучше этого:
Тут мы теряем информацию и о типе элемента списка и о типе самого списка.
Вот тот же интерфейс в стиле актуальной версии стандартной библиотеки:
public interface IList<T>
{
bool IsEmpty { get; }
T Head { get; }
IList<T> Tail { get; }
IList<T> Add(T x);
}
Теперь информацию о типе элемента мы не теряем, но информация о типе списка после приведения к интерфейсу потеряна.
А тут мы берем от дженериков все что можно:
public interface IList<T, out TL>
where TL : IList<T,TL>
{
bool IsEmpty { get; }
T Head { get; }
TL Tail { get; }
TL Add(T x);
}
Теперь мы не потеряем информацию о типах.
public static class ListHelper
{
// тут на выходе список именно того типа, который мы получили на вход public static TL AddRange<T, TL>(this TL list, IEnumerable<T> range)
where TL : IList<T, TL>
{
return range.Aggregate(list, (xs, x) => xs.Add(x));
}
// тут мы утратили информацию о типе спискаpublic static IList<T> AddRange<T>(this IList<T> list, IEnumerable<T> range)
{
return range.Aggregate(list, (xs, x) => xs.Add(x));
}
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Здравствуйте, Ахмед, Вы писали:
_>>В википедии, в статье, посвященной обобщенному програмированию, как пример, приведен кусочек кода на C#: _>>Сам по себе смысл ясен, но непонятно, чем это лучше этого: А>Ничем…
Строго говоря, разница конечно же есть. Достаточно рассмотреть случай, когда IPerson реализуется value-типом.
А>… это плохой пример.
+1
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>А зачем оно ("информация о типе списка") нужно, по большому-то счёту? AddRange из ListHelper-а можно реализовать и без этого. В каких сценариях первого объявления списка не достаточно и требуется вторая?
Допустим, что у нас есть дек, позволяющая добавлять элементы и в другой конец:
public interface IDeque<T, out TD> : IList<T, TD>
where TD : IDeque<T,TD>
{
T Last { get; }
TL Init { get; }
TL Append(T x);
}
Для дек AddRange без потери информации о типе будет работать так же хорошо, как и для списка. А на выходе метода с потерей мы останемся без методов добавления элементов в другой конец или будем вынуждены писать даун-каст.
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Здравствуйте, _FRED_, Вы писали:
_FR>Здравствуйте, Пельмешко, Вы писали:
П>>Это всё из-за того, что чтобы вызвать интерфейсный метод, структуру пришлось бы боксить, что неприемлимо в generic-коде.
_FR>Гхм: почему не приемлемо? Мне казалось, что именно так вызываются методы интерфейсов, реализованные в структуре явно. Разве нет?
Потомушта
static T Baz<T>(T t) where T : IFoo
{
t.Bar();
return t;
}
Просто этот код должен иметь такую же семантику, как если бы вместо T был конкретный тип.
Если в код простой текстовой заменой в качестве T подставить Foo:
struct Foo : IFoo
{
int x;
public void Bar() { x++; }
}
То не смотря на то, что Bar() реализует интерфейс, вызов не повлечет за собой боксинга и Bar() может изменить значение локальной переменной t, соответственно generic-код должен обладать точно такое же семантикой. Как именно дергаться будет Bar() для достижения сего эффекта — дело реализации: в случае ref-типов можно смело дёргать через интерфейс, в случае value-типов приходится компилировать специализации кода.
_FR>И, кстати, решарпер, кажется, предупреждает о возможном боксинге при сравнении переменной дженерик-типа без ограничения struct/class c null, тогда как Рихтер утверждал, что такое сравнение нормально переварится джитом и не нужный (заранее известно, что ложный) код попросту не будет скомпилирован — кто прав тут
Не видел таких suggestion'ов решарпера...
Было бы интересно увидеть подобные случаи
Самый лучший пример с дженериками — это сортировка А там я так написал, специфичную для дженерика вещь.
Здравствуйте, Klapaucius, Вы писали:
K>А тут мы берем от дженериков все что можно: K>
K>public interface IList<T, out TL>
K> where TL : IList<T,TL>
K>{
K> bool IsEmpty { get; }
K> T Head { get; }
K> TL Tail { get; }
K> TL Add(T x);
K>}
K>
Ого... Я такие чтуки даже в сети не видел.
Их специально придумали для ко-контравариаций? Что-то ещё полезное они делают?
Klapaucius, и ещё вопросик: а как подставляется аргумент в этот IList<T, out TL>? (если этот TL похож на рекурсивный тип)
В википедии, в статье, посвященной обобщенному програмированию, как пример, приведен кусочек кода на C#:
// На платформе .NET средства обобщённого программирования появились в версии 2.0.interface IPerson
{
string GetFirstName();
string GetLastName();
}
class Speaker
{
public void SpeakTo<T>(T person) where T : IPerson
{
string name = person.GetFirstName();
this.say("Hello, " + name);
}
}
Сам по себе смысл ясен, но непонятно, чем это лучше этого:
interface IPerson
{
string GetFirstName();
string GetLastName();
}
class Speaker
{
public void SpeakTo(IPerson person)
{
string name = person.GetFirstName();
this.say("Hello, " + name);
}
}
Здравствуйте, Klapaucius, Вы писали:
K>Здравствуйте, matumba, Вы писали:
M>>public void SpeakTo<T>(T person) where T : IPerson, INamedPerson, IPersonWhoHasMouth ... M>>Так смысл генериков проявляется лучше.
K>По моему, смысл дженериков проявляется лучше, если нам нужно не только передавать что-то, но и возвращать. K>Вот интерфейс списка а-ля C#1.0: K>
K>Тут мы теряем информацию и о типе элемента списка и о типе самого списка. K>Вот тот же интерфейс в стиле актуальной версии стандартной библиотеки: K>
K>Теперь информацию о типе элемента мы не теряем, но информация о типе списка после приведения к интерфейсу потеряна. K>А тут мы берем от дженериков все что можно: K>
K>public interface IList<T, out TL>
K> where TL : IList<T,TL>
K>{
K> bool IsEmpty { get; }
K> T Head { get; }
K> TL Tail { get; }
K> TL Add(T x);
K>}
K>
K>Теперь мы не потеряем информацию о типах. K>
K>public static class ListHelper
K>{
K> // тут на выходе список именно того типа, который мы получили на вход
K> public static TL AddRange<T, TL>(this TL list, IEnumerable<T> range)
K> where TL : IList<T, TL>
K> {
K> return range.Aggregate(list, (xs, x) => xs.Add(x));
K> }
K> // тут мы утратили информацию о типе списка
K> public static IList<T> AddRange<T>(this IList<T> list, IEnumerable<T> range)
K> {
K> return range.Aggregate(list, (xs, x) => xs.Add(x));
K> }
K>}
K>
Здравствуйте, Константин Л., Вы писали:
КЛ>Здравствуйте, dmitry_npi, Вы писали:
_>>В википедии, в статье, посвященной обобщенному програмированию, как пример, приведен кусочек кода на C#:
_>>
_>>// На платформе .NET средства обобщённого программирования появились в версии 2.0.
_>>interface IPerson
_>>{
_>> string GetFirstName();
_>> string GetLastName();
_>>}
_>>class Speaker
_>>{
_>> public void SpeakTo<T>(T person) where T : IPerson
_>> {
_>> string name = person.GetFirstName();
_>> this.say("Hello, " + name);
_>> }
_>>}
_>>
КЛ>afaik, тут компайлер может соптимизировать вызов GetFirstName для некоторых T
Вернее может проявиться эффект от реализации дженериков в CLR
Если T будет типом-значением, то вызов GetFirstName() будет происходить напрямую, не через интерфейс, и даже может успешно заинланиться. Это всё из-за того, что чтобы вызвать интерфейсный метод, структуру пришлось бы боксить, что неприемлимо в generic-коде.
[]
П>Вернее может проявиться эффект от реализации дженериков в CLR П>Если T будет типом-значением, то вызов GetFirstName() будет происходить напрямую, не через интерфейс, и даже может успешно заинланиться. Это всё из-за того, что чтобы вызвать интерфейсный метод, структуру пришлось бы боксить, что неприемлимо в generic-коде.
для value-типов ясно. мне казалось, что для остальных вызов может быть вызван напрямую (без callvirt?), а не через интерфейс, но могу ошибаться
Здравствуйте, Пельмешко, Вы писали:
П>Это всё из-за того, что чтобы вызвать интерфейсный метод, структуру пришлось бы боксить, что неприемлимо в generic-коде.
Гхм: почему не преемлемо? Мне казалось, что именно так вызываются методы интерфейсов, реализованные в структуре явно. Разве нет?
И, кстати, решарпер, кажется, предупреждает о возможном боксинге при сравнении переменной дженерик-типа без ограничения struct/class c null, тогда как Рихтер утверждал, что такое сравнение нормально переварится джитом и не нужный (заранее известно, что ложный) код попросту не будет скомпилирован — кто прав тут
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, matumba, Вы писали:
M>Ого... Я такие чтуки даже в сети не видел.
А я видел даже библиотеку, в которой интерфейс списка примерно так и выглядит.
M>Их специально придумали для ко-контравариаций? Что-то ещё полезное они делают?
Да, out в данном случае указатель ковариантности. Но он в данном случае не обязателен — все и без него работает.
M>а как подставляется аргумент в этот IList<T, out TL>? (если этот TL похож на рекурсивный тип)
public class List<T> : IList<T, List<T>>
{
public bool IsEmpty { ... }
public T Head { ... }
public List<T> Tail { ... }
public List<T> Add(T x) { ... }
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Здравствуйте, Klapaucius, Вы писали:
K>Тут мы теряем информацию и о типе элемента списка и о типе самого списка. K>Вот тот же интерфейс в стиле актуальной версии стандартной библиотеки:
K>Теперь информацию о типе элемента мы не теряем, но информация о типе списка после приведения к интерфейсу потеряна. K>А тут мы берем от дженериков все что можно:
K>public interface IList<T, out TL>
K> where TL : IList<T,TL>
K>{
K> bool IsEmpty { get; }
K> T Head { get; }
K> TL Tail { get; }
K> TL Add(T x);
K>}
K>Теперь мы не потеряем информацию о типах.
А зачем оно ("информация о типе списка") нужно, по большому-то счёту? AddRange из ListHelper-а можно реализовать и без этого. В каких сценариях первого объявления списка не достаточно и требуется вторая?
Help will always be given at Hogwarts to those who ask for it.
. Инлайн работает на CLR 2 32 и CLR 4 32 и 64. Сервиспаки для второй версии привносят некоторые нюансы: пример по ссылке, кажется, так быстро работать уже не будет. Проблема со статической переменной.
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Здравствуйте, ylem, Вы писали:
_FR>>Строго говоря, разница конечно же есть. Достаточно рассмотреть случай, когда IPerson реализуется value-типом.
Y>Типа, компилятор умный и боксинга не случится?
Здравствуйте, _FRED_, Вы писали:
_FR>Скорее, рантайм.
Ну, JIT — это ведь компилятор. А компилятор в байт-код — да, сгенерирует callvirt в любом случае.
... << RSDN@Home 1.2.0 alpha 4 rev. 1446>>
'You may call it "nonsense" if you like, but I'VE heard nonsense, compared with which that would be as sensible as a dictionary!' (c) Lewis Carroll
Re[5]: Простой вопрос по generic
От:
Аноним
Дата:
13.07.10 08:16
Оценка:
Здравствуйте, Пельмешко, Вы писали:
П>Если в код простой текстовой заменой в качестве T подставить Foo: П>
П>struct Foo : IFoo
П>{
П> int x;
П> public void Bar() { x++; }
П>}
П>
П>То не смотря на то, что Bar() реализует интерфейс, вызов не повлечет за собой боксинга и Bar() может изменить значение локальной переменной t, соответственно generic-код должен обладать точно такое же семантикой.
Говоришь, простая текстовая замена?
А если в структуре интерфейс реализован явно (IFoo.Bar)?
Здравствуйте, Пельмешко, Вы писали:
П>>>Это всё из-за того, что чтобы вызвать интерфейсный метод, структуру пришлось бы боксить, что неприемлимо в generic-коде. _FR>>Гхм: почему не приемлемо? Мне казалось, что именно так вызываются методы интерфейсов, реализованные в структуре явно. Разве нет? П>Потомушта
П>static T Baz<T>(T t) where T : IFoo
П>{
П> t.Bar();
П> return t;
П>}
П>Просто этот код должен иметь такую же семантику, как если бы вместо T был конкретный тип. П>Если в код простой текстовой заменой в качестве T подставить Foo:
П>struct Foo : IFoo
П>{
П> int x;
П> public void Bar() { x++; }
П>}
П>То не смотря на то, что Bar() реализует интерфейс, вызов не повлечет за собой боксинга и Bar() может изменить значение локальной переменной t, соответственно generic-код должен обладать точно такое же семантикой. Как именно дергаться будет Bar() для достижения сего эффекта — дело реализации: в случае ref-типов можно смело дёргать через интерфейс, в случае value-типов приходится компилировать специализации кода.
Я говорил про случай явной реализации структурой метода интерфейса. В случае "простой текстовой заменой" без боксинга точно будет не обойтись
Что значит "компилировать специализации кода"?
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Пельмешко, Вы писали:
П>>Если в код простой текстовой заменой в качестве T подставить Foo: П>>
П>>struct Foo : IFoo
П>>{
П>> int x;
П>> public void Bar() { x++; }
П>>}
П>>
П>>То не смотря на то, что Bar() реализует интерфейс, вызов не повлечет за собой боксинга и Bar() может изменить значение локальной переменной t, соответственно generic-код должен обладать точно такое же семантикой.
А>Говоришь, простая текстовая замена? А>А если в структуре интерфейс реализован явно (IFoo.Bar)?
Да, я ошибался... в случае явной реализации семантика изменяется...
Грустно это всё, но иначе и не может быть...
Здравствуйте, _FRED_, Вы писали:
_FR>Что значит "компилировать специализации кода"?
Я про то, что JIT в CLR генерирует n версий нативного кода (их я и назвал "специализациями") при подстановке n различных value-типов в качестве T в каком-нибудь generic-методе + одна нативная версия для всех ref-типов. К сожалению, в случае value-типов даже нельзя переиспользовать код нативный код для Baz<int>() в качестве кода Baz<uint>(), не смотря на то, что структуры идентичного размера и вообще мягко говоря очень похожи Насколько я знаю, в Mono вообще только недавно научились переиспользовать один код для всех ref-типов....