Curiously Recurring Template Pattern, Multiple Layers of Inheritance - c#

Building off of the work done here, I've defined a generic, abstract base class for enumerations, like so:
public abstract class Enumeration<T> : IEquatable<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
}
And a static, non-generic helper class to parse and list values of an enum:
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.
}
Now, I derive from this with another abstract class to represent a certain class of enumerations that have something in common:
public abstract class AnimalTrait<T> : Enumeration<AnimalTrait<T>>
{
protected AnimalTrait(int value, string displayName) : base(value, displayName) { ; }
}
So far so good. As an example the concrete class deriving from this might be DogTrait, or FishTrait, etc. Knowing that all animal traits can be paired with a value, and supposing that the value of an animal trait is always a string, I then define another abstract class like so:
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}]";
}
}
Similar to deriving from KeyValuePair<TAnimalTrait, string> where TAnimalTrait : AnimalTrait<TAnimalTrait>, which I would do if it wasn't a struct.
Now when I go to define the Animal class that holds the name of the animal and it's list of AnimalTrait's with their associated values, i.e. a list of AnimalTraitValuePair<TAnimal>, I run into a problem:
public abstract class Animal<TAnimal, TAnimalTrait> :
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(string name, IEnumerable<AnimalTraitValuePair<TAnimalTrait>> animalTraitValuePairs)
{
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();
// !!!! BREAKS HERE !!!!
foreach (var animalTrait in Enumeration.GetAllValues<AnimalTrait<TAnimalTrait>>()) // This works...
//foreach (var animalTrait in Enumeration.GetAllValues<TAnimalTrait>()) // ...but this doesn't
{
sb.AppendLine($"{this.Name}'s traits:");
sb.AppendLine($"[{animalTrait}, {animalTrait.Value}]");
}
return sb.ToString();
}
}
I get this compiler error:
The type 'TAnimalTrait' cannot be used as type parameter 'T' in the generic type or method 'Enumeration.GetAllValues<T>()'. There is no implicit reference conversion from 'TAnimalTrait' to 'Maxim.Common.Enums.Enumeration<TAnimalTrait>'
Why can't I use TAnimalTrait directly? Is not TAnimalTrait restricted to be a class of AnimalTrait<TAnimalTrait>, which we know is an Enumeration and therefore can be upcasted two levels to the base Enumeration<T>? Is the one that compiles "correct" and give me the behavior I want?

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]

You have a constraint on AnimalTraitValuePair
public struct AnimalTraitValuePair<TAnimalTrait>
where TAnimalTrait : AnimalTrait<TAnimalTrait>
When you use it you are passing in a TAnimal with Animal constraint
public abstract class Animal<TAnimal, TAnimalTrait>
: IEnumerable<AnimalTraitValuePair<TAnimal>>
where TAnimal : Animal<TAnimal, TAnimalTrait>
where TAnimalTrait : AnimalTrait<TAnimalTrait>
If you change it to the following:
public abstract class Animal<TAnimal, TAnimalTrait>
: IEnumerable<AnimalTraitValuePair<TAnimalTrait>>
where TAnimal : Animal<TAnimal, TAnimalTrait>
where TAnimalTrait : AnimalTrait<TAnimalTrait>
You will get a error stating that
Enumeration<AnimalTrait<TAnimalTrait>>.Value is inaccessable due to its protection level.
This occurs because your Animal class doesn't derive from Enumeration<AnimalTraitValuePair<TAnimalTrait>>
Honestly, being that IList<T> is a generic implementation of IEnumerable<T>, if you want a simple implementation that accomplishes the same goal, I'd just do the following:
public class Animal
{
private IList<AnimalTrait> _traits;
public Animal(IList<AnimalTrait> traits)
{
_traits = traits;
}
public IEnumerable<AnimalTrait> Traits{get{return _traits;}}
}
public class AnimalTrait
{
public int Value{get;set;}
public string DisplayName{get;set;}
}

Related

Interface needed to access overridden abstract property in classes derived from generic abstract base?

So my dilemma is that in order to access IntThing's or StringThing's MyProperty from UtilityThing<T>, I'm defining an interface with MyProperty and using it as the generic constraint on T in UtilityThing<T>. This is working, but seems redundant given that the same property is already defined in the abstract base. Am I missing a facet of design here, or is this actually the way it needs to be done in this instance?
public interface IThing {
string MyProperty { get; set; }
}
public abstract class Thing<T> {
protected string _MyProperty;
public abstract string MyProperty { get; set; }
public T OtherProperty { get; set; }
public string CommonMethod() {
return MyProperty + "foobar";
}
}
public class IntThing : Thing<int?>, IThing {
public override string MyProperty {
get { return _MyProperty; }
set { _MyProperty = value + OtherProperty.ToString(); }
}
}
public class StringThing: Thing<string>, IThing {
public override string MyProperty {
get { return _MyProperty; }
set { _MyProperty = OtherProperty + value; }
}
}
public class UtilityThing<T> where T: IThing, new() {
public T DoIt(SomeContext someContext, string name) {
string contextVal = someContext.GetValue(name);
var thing = new T { MyProperty = contextVal }
return thing;
}
}
You'll need to introduce a new generic type. Once the new type is introduced you can eliminate the need of the interface.
public class UtilityThing<T, I> where T : Thing<I>, new()
{
public T DoIt(SomeContext someContext, string name)
{
string contextVal = someContext.GetValue(name);
var thing = new T { MyProperty = contextVal };
return thing;
}
}
And you can use it like this:
var utility = new UtilityThing<IntThing, int?>();

Interface tuple key in dictionary

For all the entities in my project I have a base entity and from that interface another interface and then a class ( this is how it is and I cannot change it ).
Given any 2 objects, I want to call a method.
I have created a dictionary with a tuple key to be able to retrieve the right method.
This is the code:
public interface IAnimal
{
string Name { get; set; }
}
public interface IDog : IAnimal
{
}
public interface ICat : IAnimal
{
}
public interface IMouse : IAnimal
{
}
public class Cat : ICat
{
public string Name { get; set; }
}
public class Dog : IDog
{
public string Name { get; set; }
}
public class Mouse : IMouse
{
public string Name { get; set; }
}
public class Linker
{
private static Dictionary<Tuple<Type, Type>, object> _linkMethodsDictionary = new Dictionary<Tuple<Type, Type>, object>();
private static bool _linkDictionaryWasInitialized = false;
public void InitializeLinkMethods()
{
if (_linkDictionaryWasInitialized) return;
_linkMethodsDictionary.Add(Tuple.Create(typeof(IDog), typeof(ICat)), (Action<IDog, ICat>)LinkDogToCat);
_linkMethodsDictionary.Add(Tuple.Create(typeof(ICat), typeof(Mouse)), (Action<ICat, IMouse>)LinkCatToMouse);
_linkDictionaryWasInitialized = true;
}
public void Link<T, TU>(T entity1, TU entity2) where T : class, IAnimal
where TU : class, IAnimal
{
Action<T, TU> linkMethod = _linkMethodsDictionary[Tuple.Create(typeof(T), typeof(TU))] as Action<T, TU>;
if (linkMethod == null)
throw new NotImplementedException($"Could not find link method for {entity1.Name} and {entity2.Name}");
linkMethod(entity1, entity2);
}
public void LinkDogToCat(IDog dog, ICat cat)
{
Console.WriteLine($"Dog: {dog.Name} - Cat:{cat.Name}");
}
public void LinkCatToMouse(ICat cat, IMouse mouse)
{
Console.WriteLine($"Cat: {cat.Name} - Mouse:{mouse.Name}");
}
Not sure how to declare the key of the dictionary, because the call fails: "The given key was not present in the dictionary."
Linker linker = new Linker();
linker.InitializeLinkMethods();
ICat cat = new Cat() {Name = "The CAT"};
IDog dog = new Dog() {Name = "the DOG"};
IMouse mouse = new Mouse() {Name = "The MOUSE"};
linker.Link<ICat, IMouse>(cat, mouse);
linker.Link(dog, cat);
I have used a struct as a key for the Dictionary
internal struct EntityLinkKey
{
private readonly Type _first;
private readonly Type _second;
public EntityLinkKey(Type first, Type second)
{
this._first = first;
this._second = second;
}
public override int GetHashCode()
{
return this._first.GetHashCode() * 17 + this._second.GetHashCode();
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (GetType() != obj.GetType()) return false;
EntityLinkKey p = (EntityLinkKey)obj;
return p._first == this._first && p._second == this._second;
}
}

generic type of class property

I have the following classes:
BaseField:
public abstract class BaseField {
...
public BaseField()
{
}
public BaseField(E_FieldType fieldType)
{
_FieldType = fieldType;
}
}
TextField:
public class TextField : BaseField {
...
public TextField() : base(E_FieldType.Text)
{
}
}
DateField:
public class DateField : BaseField {
...
public DateField() : base(E_FieldType.Date)
{
}
}
And DataBlock class which should contain TextField or DateField:
public class DataBlock<T> : BaseBlock where T : BaseField, new() {
...
private T _Field;
public DataBlock(string name): base(name, E_BlockType.Data)
{
_Field = new T();
}
}
The following line works fine:
DataBlock<TextField> db = new DataBlock<TextField>("qwe");
But It is not possible to write this code:
public ObservableCollection<DataBlock<BaseField>> DataBlockList { get; set; }
public DataBlockViewModel()
{
DataBlockList = new ObservableCollection<DataBlock<BaseField>>();
DataBlockList.Add(new DataBlock<TextField>("qwe"));
DataBlockList.Add(new DataBlock<DateField>("asd"));
}
The error is:
'BaseField' must be a non-abstract type with a public parameterless
constructor in order to use it as parameter 'T' in the generic type or
method 'DataBlock<T>'
Could you please advise how to solve the issue that I can create ObservableCollection<DataBlock<BaseField>> collection?
I can remove new() from public class DataBlock<T> : BaseBlock where T : BaseField and
public DataBlock(string name): base(name, E_BlockType.Data)
{
//_Field = new T();
}
In this case I can create DataBlockList = new ObservableCollection<DataBlock<BaseField>>();
but it is not possible to write:
DataBlockList.Add(new DataBlock<TextField>("qwe"));
DataBlockList.Add(new DataBlock<DateField>("asd"));
There are 2 ways to get rid of the error:
1) You can make the class BaseField non-abstract
public abstract class BaseField {
...
public BaseField()
{
}
public BaseField(E_FieldType fieldType)
{
_FieldType = fieldType;
}
}
2) Pass the new BaseField object as constructor parameter to DataBlock and remove the new() constraint.
public class DataBlock<T> : BaseBlock where T : BaseField {
...
private T _Field;
public DataBlock(string name, T field): base(name, E_BlockType.Data)
{
_Field = field;
}
}

c# Cannot call abstract generic method from static base class method [duplicate]

I'm trying to create an abstract generic class which inherits from another abstract generic class.
Here's what I have so far
public abstract class BaseClass {
public long Id { get; private set; }
public BaseClass(long id) {
this.Id = id;
}
}
public abstract class BaseClass<T> : BaseClass where T : BaseClass {
protected BaseClass(long id)
: base(id) {
}
public static T Get(long id) {
T item;
return TryGet(id, out item) ? item : default(T);
}
public static bool TryGet(long id, out T item) {
item = null; // This is where I call the cache but for this example I've removed so it will compile
if (item != null) { return true; }
else {
// Call TryGetFallback method
return false;
}
}
protected abstract T TryGetFallback(long id);
}
public abstract class DerivedClass : BaseClass<DerivedClass> {
public String Name { get; private set; }
public DerivedClass(long id, String name)
: base(id) {
this.Name = name;
}
}
public class DerivedDerivedClass : DerivedClass {
protected override DerivedDerivedClass TryGetFallback(long id) {
// Handle the try get fallback
}
}
The TryGetFallback method on the DerivedDerivedClass causes a compiler error.
First you need to fix your BaseClass<T> implementation to not have a recursive type constraint.
public abstract class BaseClass<T> : BaseClass where T : new() {
//snip
}
Then you can use it in your derived class, for example I will make it use int for the generic type parameter:
public abstract class DerivedClass : BaseClass<int> {
//snip
}
And now if you compile it will warn you that 'DerivedDerivedClass' does not implement inherited abstract member 'BaseClass<int>.TryGetFallback(long)'
Thanks for the tips #DavidG it's helped me to solve the problem with the following code
public abstract class BaseClass {
public long Id { get; private set; }
public BaseClass(long id) {
this.Id = id;
}
}
public abstract class BaseClass<T> : BaseClass where T : BaseClass<T>, new() {
protected BaseClass(long id) : base(id) { }
public static T Get(long id) {
T item;
return TryGet(id, out item) ? item : default(T);
}
public static bool TryGet(long id, out T item) {
item = null; // Try to get item from cache here
if (item != null) { return true; }
else {
T obj = new T();
item = obj.TryGetFallback(id);
return item != null;
}
}
protected abstract T TryGetFallback(long id);
}
public abstract class DerivedClass<T> : BaseClass<T> where T : DerivedClass<T>, new() {
public String Name { get; private set; }
public DerivedClass() : base(0) { }
public DerivedClass(long id, String name)
: base(id) {
this.Name = name;
}
protected abstract override T TryGetFallback(long id);
}
public class DerivedDerivedClass : DerivedClass<DerivedDerivedClass> {
public DerivedDerivedClass() {
}
protected override DerivedDerivedClass TryGetFallback(long id) {
throw new NotImplementedException();
}
}

Generic type attribute with OR constraint

Is it possible to have a type independent attribute class, possibly decorated with an 'where or' constraint?
Example:
public class Attribute<T> where T: string || bool {
public const char ATTRIBUTE_CHAR = 'a';
public string Key { get; set; }
public T Value { get; set; }
public Attribute(string key, T value) {
Key = key;
Value = value;
}
public Attribute(string key, bool value = true)
: base(key, value) {}
public Attribute(string key, string value)
: base(key, value) {}
public override string ToString() {
if(typeof(value) == bool && (bool)value)
return String.Format({0}={1},
ATTRIBUTE_CHAR, key);
else return String.Format({0}={1}:{2},
ATTRIBUTE_CHAR, key, (string)value);
}
}
So that this:
Attribute<bool> BoolAttr1 = new Attribute<bool>("bool_1");
Attribute<bool> BoolAttr2 = new Attribute<bool>("bool_2", false);
Attribute<string> StringAttr1 = new Attribute<string>("string_1", "val1");
Attribute<string> StringAttr2 = new Attribute<string>("string_2", "val2");
...everything.ToString();
Would produce the following output
a=bool_1
a=string_1:val1
a=string_2:val2
But that something like this is never possible:
Attribute<int>...
There is no or. What you probably want to do is this:
interface IKeyValue<TKey, TValue>
{
public TKey Key {get;set;}
public TValue Value {get;set;}
}
public class Attribute : IKeyValue<string, string>
{
public override string ToString()
{
return String.Format("{0}={1}:{2}", Constants.ATTRIBUTE_CHAR, Key, Value);
}
}
public class BoolAttribute : IKeyValue<string, bool>
{
public override string ToString()
{
return Value ? String.Format("{0}={1}", Constants.ATTRIBUTE_CHAR, Key) : String.Empty;
}
}
You can create an abstract attribute and your required implementations.
Each Attribute can then be handled in the required way. (Casting to Attribute), but no Attribute of an unsupported type could be created.
public abstract class Attribute<T>
{
public abstract T getValue();
}
public class StringAttribute : Attribute<String>
{
String value;
public String getValue(){
return value;
}
}
public class BooleanAttribute : Attribute<Boolean>
{
Boolean value;
public Boolean getValue()
{
return value;
}
}
Also this allows you to implement type-dependant attribute functions very easy. (like toString() etc.)

Categories

Resources