2009-07-13 16 views
2

Wie finden Sie den folgenden Code? Ist das gut? Wenn ja, warum ist es? Wenn nicht, warum nicht? Und wie sieht CLR diesen Code?Wie diese generische Vererbung gemacht wird (intern)?

public abstract class EntityBase<TEntity> : IEquatable<TEntity> 
{   
    public bool Equals(TEntity other) 
    { 
     // check equalitiy 
    } 

    // yes, below is object's Equals and GetHashCode method implementation 
} 

public class Person : EntityBase<Person> 
{ 
} 

Ich habe ein bisschen seltsames Gefühl auf diesem. Wie Huhn und Ei Problem. Und hier ist .Net-Framework-Code, der das gleiche Verhalten hat.

public sealed class String : IComparable<string>, IEquatable<string> // I removed other interfaces 

Irgendwelche Gedanken?

+0

Ordentlich, ich wusste nicht, was Sie tun können, dass :) –

+3

Obwohl die Definition kreisförmig erscheint, ist es nicht. Der C# -Compilerzyklus-Erkennungsalgorithmus ist jedoch sowohl _wrong_ als auch _weak_. Falsch, weil es Nicht-Zyklen fälschlicherweise als Zyklen erkennt, und schwach, weil es bestimmte sehr unangenehme Zyklen nicht erkennt. Wenn dieses Thema Sie interessiert, finden Sie meinen Artikel hier: http://blogs.msdn.com/ericlippert/archive/2008/05/07/covariance-and-contravariance-part-twelve-to-infinity-but-not -beyond.aspx –

+2

Der Pseudo-Zyklus in den generischen Definitionen ist in Ordnung. Mich beunruhigt allerdings die Beziehung "Person: EntityBase ". Ich bevorzuge meine Typhierarchien, um reale Beziehungen zu modellieren: Ein Mensch ist ein Säugetier. Ein Mitarbeiter ist eine Person. Eine Zeitung ist eine Veröffentlichung. Eine Person ist eine "Entity Base of Person".Was in aller Welt bedeutet das? –

Antwort

0

Für andere Leute Referenz, kopierte ich die Antwort Eric Lippert (wer im Kommentar antworten).

Obwohl die Definition zirkulär erscheint, ist es nicht. Der C# Compiler Zyklus Erkennungsalgorithmus ist jedoch sowohl falsch als auch schwach. Falsch, weil nicht-Zyklen als Zyklen falsch erkannt und schwach ist, weil es bestimmte sehr unangenehme Zyklen erkennt. Wenn dieses Thema interessiert, finden Sie in meinem Artikel über sie hier: http://blogs.msdn.com/ericlippert/archive/2008/05/07/covariance-and-contravariance-part-twelve-to-infinity-but-not-beyond.aspx

2

Unter den richtigen Umständen (z. B. Implementierung IComparable<T>) ist es genau das Richtige zu tun.

Aber das kann nur von Fall zu Fall bestimmt werden, indem man die Details betrachtet, warum es in Betracht gezogen wird.

Auf der anderen Seite erlaubt C++ das „seltsam wiederkehrende Grundmuster“:

template<typename T> 
class SomeWrapper<T> : T { ... ] 

wo eine generische Klasse seine generische Wrapper erbt. Dies ermöglicht einige erweiterte Umhüllungsszenarien, kann jedoch schnell unübersichtlich werden, wenn sie über den Umbruch hinaus verwendet werden. Zum Glück (?) Ist dieses Muster in .NET nicht erlaubt.

+0

@Richard: Ja. Von Fall zu Fall. Aber wie denken Sie über meinen Fall nach? :) –

+0

@Soe: Nicht genug Details. Wissen, was 'Entität ' mit 'T' tut (das kann es nicht mit' this.GetType() '). – Richard

+0

@Richard: Danke für Ihre Antwort. Ich möchte IEquatable für jede Entitätsklasse implementieren. (z. B. Person), aber ich möchte nicht wiederholen. –

0

Ich sehe kein Problem mit Ihrem Code. Warum sollte es ein Problem sein?

Über die interne Darstellung kompiliert der dotNet JIT eine Klasse für jede generische Version. Also folgende:

class Foo<T> { public T Property; } 
Foo<Int> fooint; 
Foo<String> foostring; 

ist eine Art zusammengestellt in:

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; } 
+0

Der interessante Teil ist Klasse Person: EntityBase . Deshalb hat er gesagt, es ist wie ein Problem mit Hühnchen und Eiern. –

+0

Ah, ich bin es wahrscheinlich, ich mache ständig absurde Konstrukte, deshalb sehe ich das nicht als besonders: P. – Dykam

+0

Der Jitter kompiliert für jede Version nicht einen neuen. Der Jitter kompiliert einen neuen für jede Version mit einem anderen Typargument _only_, wenn das Typargument ein Werttyp ist. –

1

nicht wirklich eine Antwort, aber ich fand es irgendwie Sinn machen, nachdem ich diesen seltsamen Code geschrieben ...

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 
} 
Verwandte Themen