Как вы думаете, следующий код? Это хорошо? Если да, то почему? Если нет, то почему это не так? И как CLR видит этот код?
public abstract class EntityBase<TEntity> : IEquatable<TEntity>
{
public bool Equals(TEntity other)
{
// check equalitiy
}
// yes, below is object Equals and GetHashCode method implementation
}
public class Person : EntityBase<Person>
{
}
У меня немного странное чувство. Как проблема с курицей и яйцом. И вот код рамки .Net, который имеет такое же поведение.
public sealed class String : IComparable<string>, IEquatable<string> // I removed other interfaces
Любые мысли?
Для других людей я скопировал ответ Эрика Липперта (который отвечает в комментарии).
Хотя определение появляется круговой, это не так. Однако, С# алгоритм обнаружения циклов компиляторов как неправильные, так и слабые. Неправильно, потому что неправильно определяет нециклы как циклов и слабых, потому что не удается обнаруживают некоторые очень неприятные циклы. Если эта тема вас интересует, см. мой статью об этом здесь: http://blogs.msdn.com/ericlippert/archive/2008/05/07/covariance-and-contravariance-part-twelve-to-infinity-but-not-beyond.aspx
В правильных обстоятельствах (например, реализация IComparable<T>
) это именно то, что нужно сделать.
Но это можно определить только в каждом конкретном случае, глядя на детали того, почему он рассматривается.
С другой стороны, С++ позволяет "любопытно повторяющийся базовый шаблон":
template<typename T>
class SomeWrapper<T> : T { ... ]
где общий класс наследует свою общую оболочку. Это позволяет использовать некоторые расширенные сценарии переноса, но может быстро запутываться при использовании за пределами упаковки. К счастью (?) Этот шаблон не разрешен в .NET.
Entity<T>
делает с T
(что он не может делать с this.GetType()
).
На самом деле это не ответ, но я нашел, что это имеет смысл после того, как я написал этот странный код...
class Program
{
static void Main(string[] args)
{
var wife = new Human(Gender.Female);
var baby = wife.GiveBirth();
Console.WriteLine(baby.Gender);
Console.ReadKey();
}
}
class CanGiveBirthTo<T> where T : new()
{
public CanGiveBirthTo()
{
}
public T GiveBirth()
{
return new T();
}
}
class Human : CanGiveBirthTo<Human>
{
public Gender Gender { get; private set; }
public Human(Gender gender)
{
Gender = gender;
}
public Human()
{
Gender = RandomlyAssignAGender();
}
Gender RandomlyAssignAGender()
{
var rand = new Random();
return (Gender) rand.Next(2);
}
}
enum Gender
{
Male = 0,
Female = 1
}
Я не вижу проблемы с вашим кодом. Почему это будет проблемой?
О внутреннем представлении dotNet JIT компилирует класс для каждой родовой версии. Итак, следующее:
class Foo<T> { public T Property; }
Foo<Int> fooint;
Foo<String> foostring;
Скомпилирован в:
class FooInt { public Int Property; }
class FooString { public String Property; }
FooInt fooint;
FooString foostring;
// This is kept for if it is needed later.
// For example for generic casting, a C#4 feature.
class Foo<T> { public T Property; }