Простой вопрос по generic
От: dmitry_npi  
Дата: 11.07.10 07:38
Оценка:
В википедии, в статье, посвященной обобщенному програмированию, как пример, приведен кусочек кода на 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);
  }
}

?
Атмосферная музыка — www.aventuel.net
Re: Простой вопрос по generic
От: Ахмед  
Дата: 11.07.10 07:51
Оценка: 1 (1) +3
Здравствуйте, dmitry_npi, Вы писали:

_>В википедии, в статье, посвященной обобщенному програмированию, как пример, приведен кусочек кода на C#:


_>Сам по себе смысл ясен, но непонятно, чем это лучше этого:


Ничем, это плохой пример.
Re[2]: Простой вопрос по generic
От: hrynchuk Украина http://bookkeeper.com.ua
Дата: 12.07.10 06:29
Оценка:
Здравствуйте, Ахмед, Вы писали:

А>Ничем, это плохой пример.


Поддерживаю.
Re[2]: Простой вопрос по generic
От: _FRED_ Россия
Дата: 12.07.10 08:31
Оценка: 44 (1) +1
Здравствуйте, Ахмед, Вы писали:

_>>В википедии, в статье, посвященной обобщенному програмированию, как пример, приведен кусочек кода на C#:

_>>Сам по себе смысл ясен, но непонятно, чем это лучше этого:
А>Ничем…

Строго говоря, разница конечно же есть. Достаточно рассмотреть случай, когда IPerson реализуется value-типом.

А>… это плохой пример.


+1
Help will always be given at Hogwarts to those who ask for it.
Re: Простой вопрос по generic
От: matumba  
Дата: 12.07.10 09:44
Оценка: 17 (1)
Здравствуйте, dmitry_npi, Вы писали:

_> public void SpeakTo<T>(T person) where T : IPerson


public void SpeakTo<T>(T person) where T : IPerson, INamedPerson, IPersonWhoHasMouth ...

Так смысл генериков проявляется лучше.
Re[2]: Простой вопрос по generic
От: nikov США http://www.linkedin.com/in/nikov
Дата: 12.07.10 09:56
Оценка:
Здравствуйте, matumba, Вы писали:

M>public void SpeakTo<T>(T person) where T : IPerson, INamedPerson, IPersonWhoHasMouth ...


M>Так смысл генериков проявляется лучше.


Это из-за того, что в C# нет compound types, как в Scala. Иначе даже в таком примере генерики бы были не нужны.
Re[2]: Простой вопрос по generic
От: Klapaucius  
Дата: 12.07.10 10:45
Оценка: 17 (1) +2
Здравствуйте, matumba, Вы писали:

M>public void SpeakTo<T>(T person) where T : IPerson, INamedPerson, IPersonWhoHasMouth ...

M>Так смысл генериков проявляется лучше.

По моему, смысл дженериков проявляется лучше, если нам нужно не только передавать что-то, но и возвращать.
Вот интерфейс списка а-ля C#1.0:
public interface IList
{
        bool IsEmpty { get; }
        object Head { get; }
        IList Tail { get; }
        IList Add(object x);
}

Тут мы теряем информацию и о типе элемента списка и о типе самого списка.
Вот тот же интерфейс в стиле актуальной версии стандартной библиотеки:
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
Re[3]: Простой вопрос по generic
От: dmitry_npi  
Дата: 12.07.10 18:20
Оценка:
Здравствуйте, Klapaucius, Вы писали:

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


M>>public void SpeakTo<T>(T person) where T : IPerson, INamedPerson, IPersonWhoHasMouth ...

M>>Так смысл генериков проявляется лучше.

K>По моему, смысл дженериков проявляется лучше, если нам нужно не только передавать что-то, но и возвращать.

K>Вот интерфейс списка а-ля C#1.0:
K>
K>public interface IList
K>{
K>        bool IsEmpty { get; }
K>        object Head { get; }
K>        IList Tail { get; }
K>        IList Add(object x);
K>}
K>

K>Тут мы теряем информацию и о типе элемента списка и о типе самого списка.
K>Вот тот же интерфейс в стиле актуальной версии стандартной библиотеки:
K>
K>public interface IList<T>
K>{
K>        bool IsEmpty { get; }
K>        T Head { get; }
K>        IList<T> Tail { get; }
K>        IList<T> Add(T x);
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>


медитирую...
Атмосферная музыка — www.aventuel.net
Re: Простой вопрос по generic
От: Константин Л. Россия  
Дата: 12.07.10 18:48
Оценка:
Здравствуйте, 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

[]
Estuve en Granada y me acorde' de ti
Re[3]: Простой вопрос по generic
От: matumba  
Дата: 12.07.10 21:25
Оценка: :)
Самый лучший пример с дженериками — это сортировка А там я так написал, специфичную для дженерика вещь.

Здравствуйте, 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 похож на рекурсивный тип)
Re[2]: Простой вопрос по generic
От: Пельмешко Россия blog
Дата: 12.07.10 22:07
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>Здравствуйте, 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-коде.
Re[3]: Простой вопрос по generic
От: Константин Л. Россия  
Дата: 12.07.10 22:33
Оценка:
Здравствуйте, Пельмешко, Вы писали:

[]

П>Вернее может проявиться эффект от реализации дженериков в CLR

П>Если T будет типом-значением, то вызов GetFirstName() будет происходить напрямую, не через интерфейс, и даже может успешно заинланиться. Это всё из-за того, что чтобы вызвать интерфейсный метод, структуру пришлось бы боксить, что неприемлимо в generic-коде.

для value-типов ясно. мне казалось, что для остальных вызов может быть вызван напрямую (без callvirt?), а не через интерфейс, но могу ошибаться
Estuve en Granada y me acorde' de ti
Re[3]: Простой вопрос по generic
От: ylem  
Дата: 12.07.10 23:18
Оценка:
_FR>Строго говоря, разница конечно же есть. Достаточно рассмотреть случай, когда IPerson реализуется value-типом.

Типа, компилятор умный и боксинга не случится?
Re[3]: Простой вопрос по generic
От: _FRED_ Россия
Дата: 13.07.10 06:03
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>Это всё из-за того, что чтобы вызвать интерфейсный метод, структуру пришлось бы боксить, что неприемлимо в generic-коде.


Гхм: почему не преемлемо? Мне казалось, что именно так вызываются методы интерфейсов, реализованные в структуре явно. Разве нет?

И, кстати, решарпер, кажется, предупреждает о возможном боксинге при сравнении переменной дженерик-типа без ограничения struct/class c null, тогда как Рихтер утверждал, что такое сравнение нормально переварится джитом и не нужный (заранее известно, что ложный) код попросту не будет скомпилирован — кто прав тут
Help will always be given at Hogwarts to those who ask for it.
Re[4]: Простой вопрос по generic
От: Klapaucius  
Дата: 13.07.10 06:06
Оценка:
Здравствуйте, 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
Re[3]: Простой вопрос по generic
От: _FRED_ Россия
Дата: 13.07.10 06:08
Оценка:
Здравствуйте, Klapaucius, Вы писали:

K>Тут мы теряем информацию и о типе элемента списка и о типе самого списка.

K>Вот тот же интерфейс в стиле актуальной версии стандартной библиотеки:
K>public interface IList<T>
K>{
K>        bool IsEmpty { get; }
K>        T Head { get; }
K>        IList<T> Tail { get; }
K>        IList<T> Add(T x);
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.
Re[4]: Простой вопрос по generic
От: Klapaucius  
Дата: 13.07.10 06:12
Оценка:
Здравствуйте, ylem, Вы писали:

Y>Типа, компилятор умный и боксинга не случится?


Не только боксинга не случится, но и вызовы методов value-типа будут заинлайнены
Автор: Klapaucius
Дата: 23.03.07
. Инлайн работает на 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
Re[4]: Простой вопрос по generic
От: _FRED_ Россия
Дата: 13.07.10 06:14
Оценка:
Здравствуйте, ylem, Вы писали:

_FR>>Строго говоря, разница конечно же есть. Достаточно рассмотреть случай, когда IPerson реализуется value-типом.


Y>Типа, компилятор умный и боксинга не случится?


Скорее, рантайм. И, может быть
Автор: _FRED_
Дата: 13.07.10
, не случится.
Help will always be given at Hogwarts to those who ask for it.
Re[4]: Простой вопрос по generic
От: Klapaucius  
Дата: 13.07.10 06:28
Оценка: 20 (1)
Здравствуйте, _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
Re[5]: Простой вопрос по generic
От: Klapaucius  
Дата: 13.07.10 06:31
Оценка:
Здравствуйте, _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
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.