Curiously Recurring Template Pattern and Generics Constraints (C#)

Curiously Recurring Template Pattern and generics constraints (C#)

This might work for you:

class Base<T> where T : Base<T>

You can't constrain T to an open generic type. If you need to constrain T to Base<whatever>, you'll need to construct something like:

abstract class Base { }

class Base<T> : Base where T : Base { ... }

Curiously Recurring Template Pattern, Multiple Layers of Inheritance

There were a number of problems with your code, and I lost track of all the things I had to change, but here's a working snippet:

void Main()
{
Console.WriteLine(Dog.Fido.ToString());
}

public abstract class Enumeration<T> where T : Enumeration<T>
{
private static IEnumerable<T> enumerateAllValues()
{
// Obviously use some caching here
var fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
return fields.Select(f => f.GetValue(null)).OfType<T>();
}

internal static IEnumerable<T> AllValues { get { return enumerateAllValues();}}

protected Enumeration(int value, string displayName)
{
if (!typeof(T).IsSealed)
throw new NotSupportedException($"Value objects must be sealed, {typeof(T).Name} is not.");
this.Value = value;
this.DisplayName = displayName;
}

protected int Value { get; }

protected string DisplayName { get; }

public override string ToString() { return DisplayName; }
// IEquatable implementation based solely on this.Value
}

public static class Enumeration
{
public static IEnumerable<T> GetAllValues<T>() where T : Enumeration<T>
{
return Enumeration<T>.AllValues;
}
// Other helper methods, e.g. T Parse(int), bool TryParse(int, out T), etc.
}

public abstract class AnimalTrait<T> : Enumeration<T>
where T : AnimalTrait<T>
{
protected AnimalTrait(int value, string displayName) : base(value, displayName) {; }
}

public struct AnimalTraitValuePair<TAnimalTrait> where TAnimalTrait : AnimalTrait<TAnimalTrait>
{

public TAnimalTrait AnimalTrait { get; }

public string Value { get; } // Analogy breaks down here, but lets assume we know that the values of animal traits are always strings.

public AnimalTraitValuePair(TAnimalTrait animalTrait, string value)
{
this.AnimalTrait = animalTrait;
this.Value = value;
}

public override string ToString()
{
return $"[{AnimalTrait}, {Value}]";
}
}

public abstract class Animal<TAnimal, TAnimalTrait> : Enumeration<TAnimal>
where TAnimal : Animal<TAnimal, TAnimalTrait>
where TAnimalTrait : AnimalTrait<TAnimalTrait>
{
private readonly IList<AnimalTraitValuePair<TAnimalTrait>> animalTraitValuePairList;

// All animals have a name
public string Name { get; }

protected Animal(int i, string name, IEnumerable<AnimalTraitValuePair<TAnimalTrait>> animalTraitValuePairs)
: base(i, name)
{
animalTraitValuePairList = animalTraitValuePairs.ToList();
this.Name = name;
}

public string this[TAnimalTrait animalTrait]
{
get
{
return animalTraitValuePairList.First(atvp => atvp.AnimalTrait == animalTrait).Value;
}
}

public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"{this.Name}'s traits:");
foreach (var animalTrait in Enumeration.GetAllValues<TAnimalTrait>())
{
sb.AppendLine($"[{animalTrait}, {this[animalTrait]}]");
}
return sb.ToString();
}
}

public sealed class DogTrait : AnimalTrait<DogTrait>
{
public DogTrait(int i, string name)
: base(i, name)
{ }
public static DogTrait Color = new DogTrait(1, "Color");
public static DogTrait Size = new DogTrait(2, "Size");
}

public sealed class Dog : Animal<Dog, DogTrait>
{
public Dog(int i, string name, IEnumerable<AnimalTraitValuePair<DogTrait>> animalTraitValuePairs)
: base(i, name, animalTraitValuePairs)
{
}
public static Dog Fido = new Dog(1, "Fido", new[] {
new AnimalTraitValuePair<DogTrait>(DogTrait.Color, "Black"),
new AnimalTraitValuePair<DogTrait>(DogTrait.Size, "Medium"),
});
}

Output:

Fido's traits:

[Color, Black]

[Size, Medium]

C# unusual inheritance syntax w/ generics

That's a funny Curiously Recurring Template Pattern, isn't it?

Curiously recurring template pattern constraints with Scala abstract types

class Binning[V <: Container[V] with Aggregation](val v: V) extends Container[Binning[V]] with Aggregation {
type Ed = Binned[v.Ed]
def stuff = this
def substuff = v
def fill(x: Double) { }
}

works. But it does seem to me like your version should compile as well.

c# templates how can i apply constraints to a class that derives from something

class TVIRoot<TLabelHandler> : OURTreeNodeImpl where TLabelHandler : SomeClass { } //yes

Best way to refer to my own type

The pattern you are using does not actually implement the constraint you want. Suppose you want to model "an animal can only be friendly with something of its own kind":

abstract class Animal<T> where T : Animal<T>
{
public abstract void GetFriendly(T t);
}

class Cat : Animal<Cat>
{
public override void GetFriendly(Cat cat) {}
}

Have we succeeded in implementing the desired constraint? No.

class EvilDog : Animal<Cat>
{
public override void GetFriendly(Cat cat) {}
}

Now an evil dog can be friendly with any Cat, and not friendly with other evil dogs.

The type constraint you want is not possible in the C# type system. Try Haskell if you need this sort of constraint enforced by the type system.

See my article on this subject for more details:

http://blogs.msdn.com/b/ericlippert/archive/2011/02/03/curiouser-and-curiouser.aspx

Inheriting Serialization using the Curiously Recurring Template Pattern

MenuChoiceCommand inherits Command, which inherits XMLBackedObject<Command>, not XMLBackedObject<MenuChoiceCommand>. So the serializer created by Save is for type Command, not MenuChoiceCommand... You would need to make MenuChoiceCommand inherit XMLBackedObject<MenuChoiceCommand> for this to work (but then you wouldn't be able to make it inherit Command, since C# doesn't allow multiple inheritance).

Using the curiously recurring template pattern for this might seem a good idea at first glance, but as you can see, you can quickly encounter its limitations.

Anyway, I don't think the serialization logic should be part of the data class itself; it would probably be better to do it in a helper class with generic methods:

public static class XmlHelper
{
public static T Load<T>(string filePath)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using(FileStream stream = new FileStream(filePath, FileMode.Open))
{
return (T)serializer.Deserialize(stream);
}
}

public static void Save<T>(T obj, string filePath)
{
XmlSerializer serializer = new XmlSerializer(typeof(T));
using(FileStream stream = new FileStream(filePath, FileMode.Create))
{
serializer.Serialize(stream, obj);
}
}
}


Related Topics



Leave a reply



Submit