Вызов "неизвестного" метода из шаблона
От: KA it-knowledge-base.blogspot.com
Дата: 03.01.11 15:10
Оценка:
Всем привет,

есть код:
using System.Collections.Generic;

namespace EqualsTest
{
  class Super
  {
    public void Everything() { }
  }
  class Container<T> : List<T> where T: class
  {
    public void DoEverything()
    {
      foreach (T item in this)
        item.Everything();
    }
  }
  class Program
  {
    static void Main(string[] args)
    {
      Container<Super> con1 = new Container<Super>();
      con1.Add(new Super());
      con1.DoEverything();
    }
  }
}

который не компилируется из-за строки item.Everything() с ошибкой

UnknownTest.cs(14,14): error CS1061: 'T' does not contain a definition for 'Everything' and no extension method 'Everything' accepting a first argument of type 'T' could be found (are you missing a using directive or an assembly reference?)


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

Заранее благодарю за ц.у. )
//
#import <windows.bas>
class IWindows9x:protected DOS { private: virtual HANDLE EnumClouds()=0; };
Re: Вызов "неизвестного" метода из шаблона
От: rasp_file Украина  
Дата: 03.01.11 15:18
Оценка: 4 (1)
Здравствуйте, KA, Вы писали:

KA>Всем привет,

KA>Что надо скормить компилятору, чтобы подобные конструкции заработали?
KA>Не хотелось бы вводить для этого интерфейс.

В constraint прописать базовый класс для итемов? Хотя, от введения интерефейса и его прописывания в constraint не сильно отличается.
class Container<T> : List<T> where T: Super
{
    public void DoEverything()
    {
        foreach (T item in this)
            item.Everything();
    }
}

Или заюзать dynamic (в C# 4)
class Container<T> : List<T> where T : class
{
        public void DoEverything()
        {
            
            foreach (dynamic item in this)
            {
                item.Everything();
            }
                
        }
}
... << RSDN@Home 1.2.0 alpha 4 rev. 1481>>
Re: Вызов "неизвестного" метода из шаблона
От: rasp_file Украина  
Дата: 03.01.11 15:31
Оценка: +1
Здравствуйте, KA, Вы писали:

KA>Что надо скормить компилятору, чтобы подобные конструкции заработали?

KA>Не хотелось бы вводить для этого интерфейс.
конечно, вопрос из области теории но зачем так делать?
в одном месте:
      foreach (T item in this)
        item.Everything();

вы подразумеваете что элементы контейнера должны соответствовать (имеют метод everything) определенному контракту, но явно указывать это
(вводя интерфейс и указывать, в виде generic constraint, например) это не хотите. при том про динамическую типизацию и duck typing не пишите. что-то не так наверное.


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


generics реализованы не как templates в C++. грубо говоря никакой текстовой подстановки и инстанцирования нет.
... << RSDN@Home 1.2.0 alpha 4 rev. 1481>>
Re[2]: Вызов "неизвестного" метода из шаблона
От: KA it-knowledge-base.blogspot.com
Дата: 03.01.11 16:30
Оценка:
Здравствуйте, rasp_file, Вы писали:

KA>>Что надо скормить компилятору, чтобы подобные конструкции заработали?

KA>>Не хотелось бы вводить для этого интерфейс.

_>Или заюзать dynamic (в C# 4)

_>
_>class Container<T> : List<T> where T : class
_>{
_>        public void  DoEverything()
_>        {
            
_>            foreach (dynamic item in this)
_>            {
_>                item.Everything();
_>            }
                
_>        }
_>}
_>


Аха, интересный вариант, но учитывая, что dynamic "отключает" проверку типов во время компиляции, мне он не очень нравится — во время компиляции в моём случае уже всё известно...
//
#import <windows.bas>
class IWindows9x:protected DOS { private: virtual HANDLE EnumClouds()=0; };
Re[2]: Вызов "неизвестного" метода из шаблона
От: KA it-knowledge-base.blogspot.com
Дата: 03.01.11 16:42
Оценка:
Здравствуйте, rasp_file, Вы писали:

KA>>Что надо скормить компилятору, чтобы подобные конструкции заработали?

KA>>Не хотелось бы вводить для этого интерфейс.
_>конечно, вопрос из области теории но зачем так делать?

Я привёл упрощенный пример.
В реальной программе код чуток более наворочен и выглядит приблизительно так:
using System.Collections.Generic;

namespace EqualsTest
{
  interface IBase
  {
    void Everything();
  }
  class Super : IBase
  {
    public void Everything() { }
    public bool Equals(Super other) { return true; }
  }
  class Container<T> : List<T> where T: IBase
  {
    public bool Equals(Container<T> other)
    {
      if (Count != other.Count)
        return false;
      for (int i = 0; i < Count; ++i)
        if (!this[i].Equals(other[i])) // always calls System.Object.Equals(Object)
          return false;
      return false;
    }
  }
  class Program
  {
    static void Main(string[] args)
    {
      Container<Super> con1 = new Container<Super>();
      con1.Add(new Super());
      Container<Super> con2 = new Container<Super>();
      con2.Add(new Super());
      System.Console.WriteLine("{0}", con1.Equals(con2));
    }
  }
}

Этот чудо-код компилируется, но всегда выдаёт False, поскольку всегда вызывается метод System.Object.Equals, который сравнивает значения ссылок.
Как этот фрагмент можно было бы переделать с учётом особенностей generics in C#?

_>generics реализованы не как templates в C++. грубо говоря никакой текстовой подстановки и инстанцирования нет.


Эх, ну, жаль, что подход шаблонов в стиле C++ не доступен в C#...
//
#import <windows.bas>
class IWindows9x:protected DOS { private: virtual HANDLE EnumClouds()=0; };
Re[3]: Вызов "неизвестного" метода из шаблона
От: Jolly Roger  
Дата: 03.01.11 17:57
Оценка: +1
Здравствуйте, KA, Вы писали:

KA>Этот чудо-код компилируется, но всегда выдаёт False, поскольку всегда вызывается метод System.Object.Equals, который сравнивает значения ссылок.


  class Super : IBase
  {
    public void Everything() { }
    public override bool Equals(Super other) { return true; }
  }
"Нормальные герои всегда идут в обход!"
Re[4]: Вызов "неизвестного" метода из шаблона
От: Sinix  
Дата: 03.01.11 18:06
Оценка: 2 (1) +2
Здравствуйте, Jolly Roger, Вы писали:

  class Super : IBase
  {
    public void Everything() { }
    public override bool Equals(Super other) { return true; }
  }


ТС ещё кое-что забыл:
+ IEquatable<IBase>
+ GetHashCode()
+ операторы
+ sealed Super или аккуратная реализация операторов + override Equals и GetHashCode во всех наследниках.

Для простых случаев проще использовать свой IEqualityComparer<IBase>.
Re[4]: Вызов "неизвестного" метода из шаблона
От: Jolly Roger  
Дата: 03.01.11 18:07
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

Не обратил внимания

JR>
JR>    public override bool Equals(Super other) { return true; }
JR>


Подчёркнутое конечно не пройдёт, нужно object.
"Нормальные герои всегда идут в обход!"
Re[5]: Вызов "неизвестного" метода из шаблона
От: Jolly Roger  
Дата: 03.01.11 18:09
Оценка: +1
Здравствуйте, Sinix, Вы писали:

Да, конечно. Я просто отвечал, почему у него в данном случае вызывается Object.Equals, а не определённый в классе Super
"Нормальные герои всегда идут в обход!"
Re[5]: Вызов "неизвестного" метода из шаблона
От: KA it-knowledge-base.blogspot.com
Дата: 03.01.11 18:20
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

JR>Не обратил внимания


JR>>
JR>>    public override bool Equals(Super other) { return true; }
JR>>


JR>Подчёркнутое конечно не пройдёт, нужно object.


Хм. Спасибо за исправления, но меня не интересует сравнение через System.Object — мне придётся много методов переопределять.
В generic-контейнерах у меня хранятся определённые объекты, и я хочу сравнивать их с объектами того же типа.
Поэтому путь через переопределение стандартного метода Equals и GetHashCode — это слишком громоздко.
Есть ли способ попроще?
//
#import <windows.bas>
class IWindows9x:protected DOS { private: virtual HANDLE EnumClouds()=0; };
Re[6]: Вызов "неизвестного" метода из шаблона
От: Jolly Roger  
Дата: 03.01.11 18:32
Оценка:
Здравствуйте, KA, Вы писали:

KA>Поэтому путь через переопределение стандартного метода Equals и GetHashCode — это слишком громоздко.

KA>Есть ли способ попроще?

Есть, но о нём Вам уже говорили — class Container<T> : List<T> where T: Super. Либо интерфейс. Дженерики — это не шаблоны C++, совсем.
"Нормальные герои всегда идут в обход!"
Re[7]: Вызов "неизвестного" метода Equals из шаблона
От: KA it-knowledge-base.blogspot.com
Дата: 05.01.11 17:15
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

KA>>Поэтому путь через переопределение стандартного метода Equals и GetHashCode — это слишком громоздко.

KA>>Есть ли способ попроще?

JR>Есть, но о нём Вам уже говорили — class Container<T> : List<T> where T: Super. Либо интерфейс. Дженерики — это не шаблоны C++, совсем.


Оказывается, метод попроще всё-таки есть.
Как верно подметили в этой теме ранее, он реализуется через интерфейс, вернее через стандартный generic-interface IEquatable&lt;T&gt;.
Ранее приведенный кусок кода надо дополнить всего парой строк (выделены жирным шрифтом), а именно:
using System.Collections.Generic;

namespace EqualsTest
{
  interface IBase
  {
    void Everything();
  }
  class Super : IBase, System.IEquatable<Super>
  {
    public void Everything() { }
    public bool Equals(Super other) { return true; }
  }
  class Container<T> : List<T> where T: IBase, System.IEquatable<T>
  {
    public bool Equals(Container<T> other)
    {
      if (Count != other.Count)
        return false;
      for (int i = 0; i < Count; ++i)
        if (!this[i].Equals(other[i]))
          return false;
      return true;
    }
  }
  class Program
  {
    static void Main(string[] args)
    {
      Container<Super> con1 = new Container<Super>();
      con1.Add(new Super());
      Container<Super> con2 = new Container<Super>();
      con2.Add(new Super());
      System.Console.WriteLine("{0}", con1.Equals(con2));
    }
  }
}


Всем спасибо за соучастие
//
#import <windows.bas>
class IWindows9x:protected DOS { private: virtual HANDLE EnumClouds()=0; };
Re[8]: Вызов "неизвестного" метода Equals из шаблона
От: Jolly Roger  
Дата: 05.01.11 17:33
Оценка:
Здравствуйте, KA, Вы писали:

KA>Оказывается, метод попроще всё-таки есть.

KA>Как верно подметили в этой теме ранее, он реализуется через интерфейс, вернее через стандартный generic-interface IEquatable&lt;T&gt;.

JR>>Есть, но о нём Вам уже говорили — class Container<T> : List<T> where T: Super. Либо интерфейс.

"Нормальные герои всегда идут в обход!"
Re[8]: Вызов "неизвестного" метода Equals из шаблона
От: HowardLovekraft  
Дата: 05.01.11 19:31
Оценка: 2 (1) +1
Здравствуйте, KA, Вы писали:

KA>Оказывается, метод попроще всё-таки есть.

KA>он реализуется через интерфейс IEquatable&lt;T&gt;.
KA>Ранее приведенный кусок кода надо дополнить всего парой строк (выделены жирным шрифтом), а именно:
KA>
KA>  class Super : IBase, System.IEquatable<Super>
KA>  {
KA>    public void Everything() { }
KA>    public bool Equals(Super other) { return true; }
KA>  }
KA>}

До конца не дочитали?

Примечания для разработчиков

Если реализуется интерфейс IEquatable<T>, следует также переопределить реализацию базового класса для методов Object.Equals(Object) и GetHashCode, чтобы их поведение соответствовало поведению метода IEquatable<T>.Equals.

Нифига это не проще. См. здесь
Автор: Sinix
Дата: 03.01.11
:

Для простых случаев проще использовать свой IEqualityComparer<IBase>

Re[9]: Вызов "неизвестного" метода Equals из шаблона
От: KA it-knowledge-base.blogspot.com
Дата: 05.01.11 19:34
Оценка:
Здравствуйте, Jolly Roger, Вы писали:

KA>>Оказывается, метод попроще всё-таки есть.

KA>>Как верно подметили в этой теме ранее, он реализуется через интерфейс, вернее через стандартный generic-interface IEquatable&lt;T&gt;.

JR>>>Есть, но о нём Вам уже говорили — class Container<T> : List<T> where T: Super. Либо интерфейс.

JR>

миль пардон. Я, как начинающий в C#, не догадался, что вы имели в виду именно интерфейс IEquatable
//
#import <windows.bas>
class IWindows9x:protected DOS { private: virtual HANDLE EnumClouds()=0; };
Re[9]: Вызов "неизвестного" метода Equals из шаблона
От: KA it-knowledge-base.blogspot.com
Дата: 05.01.11 19:55
Оценка:
Здравствуйте, HowardLovekraft, Вы писали:

KA>>Оказывается, метод попроще ...

KA>> реализуется через интерфейс IEquatable&lt;T&gt;.
KA>>Ранее приведенный кусок кода надо дополнить всего парой строк (выделены жирным шрифтом), а именно:
KA>>
KA>>  class Super : IBase, System.IEquatable<Super>
KA>>  {
KA>>    public void Everything() { }
KA>>    public bool Equals(Super other) { return true; }
KA>>  }
KA>>}

HL>До конца не дочитали?
HL>

HL>Примечания для разработчиков

HL>Если реализуется интерфейс IEquatable<T>, следует также переопределить реализацию базового класса для методов Object.Equals(Object) и GetHashCode, чтобы их поведение соответствовало поведению метода IEquatable<T>.Equals.

HL>Нифига это не проще. См. здесь
Автор: Sinix
Дата: 03.01.11
:

HL>

HL>Для простых случаев проще использовать свой IEqualityComparer<IBase>


Эээ... спасибо за подсказку.
Хоть это и не следует из приведенного мною фрагмента кода, в моём случае идёт сравнение только однотипных объектов, т.е. я не собираюсь сравнивать Super с каким-то гипотетическим классом Super2, а только Super — c Super'ом, и Super2 — c Super2.
Если же пользователь захочет сравнивать через Equals(Object), то эту вредную наклонность проще пресечь сокрытием данного метода, или выбрасыванием исключения, ибо нефиг (это не имеет смысла в предметной области, которая моделируется программой).

EqualityComparer — интересный класс. Возможно, его применение и оправдано в более сложных случаях. Пока что я не вижу необходимости выносить сравнение из Super.Equals(Super) в EqualityComparer.Equals(Super,Super).
//
#import <windows.bas>
class IWindows9x:protected DOS { private: virtual HANDLE EnumClouds()=0; };
Re[10]: Вызов "неизвестного" метода Equals из шаблона
От: Jolly Roger  
Дата: 06.01.11 01:29
Оценка:
Здравствуйте, KA, Вы писали:

KA>миль пардон. Я, как начинающий в C#, не догадался, что вы имели в виду именно интерфейс IEquatable


Я имел в виду любой интерфейс.
"Нормальные герои всегда идут в обход!"
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.