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.)
Related
I would like to create a generic list which takes in a generic class. But the problem is i can't access the variables inside the generic class and i've seen people leave the interface empty and wonder how i should do to access the variables in the class.
public List<IAttributeInterface> attributes = new List<IAttributeInterface>();
public void AddAttribute<T>(T attribute, T value)
for (int i = 0; i < attributes.Count; i++)
{
if (attributes[i].key == attribute)
{
attributes[i].value = value;
return;
}
}
attributes.Add(new Attribute(attribute, value));
}
public class Attribute<T> where T : IAttributeInterface
{
public T key;
public T value;
public Attribute(T key, T value)
{
this.key = key;
this.value = value;
}
}
public interface IAttributeInterface
{
}
It is necessary to add properties inside of interfaces:
public interface IAttributeInterface<T>
{
T Key { get; set; }
T Value { get; set; }
}
Then we need to implement this generic interface in class to be able to create instance of object:
public class Attribute<T> : IAttributeInterface<T>
{
public T Key { get; set ; }
public T Value { get ; set; }
public Attribute(T key, T value)
{
this.Key = key;
this.Value = value;
}
}
and your method will look like this:
public List<IAttributeInterface<string>> attributes =
new List<IAttributeInterface<string>>();
public void AddAttribute<T>(T attribute, T value) where T: IAttributeInterface<string>
{
for (int i = 0; i < attributes.Count; i++)
{
if (attributes[i].Key == attribute.Key)
{
attributes[i].Value = value.Value;
return;
}
}
attributes.Add(new Attribute<string>("key", "value"));
}
and it is possible to use like that:
AddAttribute(new Attribute<string>("foo key 0", "foo value 0"),
new Attribute<string>("foo key 1", "foo value 1"));
You need to add {get;set;} for those attributes (properties) in the interface.
public interface IAttributeInterface<T>
{
T Key { get; set; }
T Value { get; set; }
}
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?>();
I need to design a data structure that holds different types of values (doubles, strings, datetimes, etc.). The list of types is dynamically created by user. Based on that list another list of values should be created.
Then this "record" of values is to be sent by WCF and stored in dynamically created db table. I'm starting with desiging this solution in c#. My current status is shown below. I'm not satisfied with my present solution, especially with factory and enums. Is there better way to do the things right?
Enum for my types:
public enum ValueType { Decimal, String, Boolean };
then interface:
public interface IValueType
{
object Data { get; }
string ToString();
ValueType? Type { get; }
}
base class:
public abstract class ValueType<T> : IValueType
{
protected T _Value;
public ValueType(T value)
{
_Value = value;
}
public object Data
{
get { return _Value; }
}
public ValueType? Type
{
get { return null; }
}
public T Value { get; private set; }
public override string ToString()
{
return _Value.ToString();
}
}
one of implementation:
public class DecimalValueType : ValueType<decimal>
{
public DecimalValueType( decimal val ) : base(val)
{}
public DecimalValueType(double val) : base((decimal)val)
{}
public DecimalValueType(int val) : base((decimal)val)
{}
}
then factory:
public static class ValueTypeFactory
{
private static Dictionary<ValueType, Type> dictValueType = new Dictionary<ValueType, Type>()
{
{ ValueType.Decimal, typeof(DecimalValueType) },
{ ValueType.String, typeof(StringValueType) },
{ ValueType.Boolean, typeof(BooleansValueType) }
};
private static Dictionary<Type, Type> dictSimple = new Dictionary<Type, Type>()
{
{ typeof(decimal), typeof(DecimalValueType) },
{ typeof(double), typeof(DecimalValueType) },
{ typeof(int), typeof(DecimalValueType) },
{ typeof(string), typeof(StringValueType) },
{ typeof(bool), typeof(BooleansValueType) }
};
public static IValueType MakeByValueType(ValueType type, params object[] initValues)
{
IValueType retObject = null;
if (dictValueType.ContainsKey(type) )
{
Type t = dictValueType[type];
retObject = (IValueType)Activator.CreateInstance(t,initValues);
}
return retObject;
}
public static IValueType MakeByType(params object[] initValues)
{
IValueType retObject = null;
if ( initValues.Length > 0 )
{
Type type = initValues[0].GetType();
if (dictSimple.ContainsKey(type))
{
Type t = dictSimple[type];
retObject = (IValueType)Activator.CreateInstance(t, initValues);
}
}
return retObject;
}
}
sample use:
List<IValueType> lista = new List<IValueType>();
lista.Add(new DecimalValueType(12));
lista.Add(new StringValueType("Test"));
lista.Add(new BooleansValueType(true));
lista.Add(ValueTypeFactory.MakeByValueType(ValueType.Decimal, 10.1));
lista.Add(ValueTypeFactory.MakeByType(5.12));
lista.Add(ValueTypeFactory.MakeByType("Test2"));
I would be happy with any advice.
Here is a simpler solution that covers the usages in your post and avoids the ValueType subclass noise:
public abstract class ValueType
{
public enum Types { Decimal, String, Boolean };
public abstract object Data { get; }
public abstract Types Type { get; }
private ValueType() {}
protected class TypedValueType<T> : ValueType
{
private Types type;
public TypedValueType(T value, Types type) : base()
{
this.Value = value;
this.type = type;
}
public override object Data { get { return this.Value; } }
public override Types Type { get { return this.type; } }
public T Value { get; private set; }
public override string ToString()
{
return this.Value.ToString();
}
}
public static implicit operator ValueType(decimal value) { return new TypedValueType<decimal>(value, Types.Decimal); }
public static implicit operator ValueType(double value) { return new TypedValueType<decimal>((decimal)value, Types.Decimal); }
public static implicit operator ValueType(int value) { return new TypedValueType<decimal>((decimal)value, Types.Decimal); }
public static implicit operator ValueType(string value) { return new TypedValueType<string>(value, Types.String); }
public static implicit operator ValueType(bool value) { return new TypedValueType<bool>(value, Types.Boolean); }
}
Sample usage:
public class Demo
{
public static void Main()
{
List<ValueType> lista = new List<ValueType>();
lista.Add(1);
lista.Add("Test");
lista.Add(true);
lista.Add(10.1);
lista.Add(5.12);
lista.Add("Test2");
foreach(var value in lista) Console.WriteLine(value.Data + " - " + value.Type.ToString());
Console.ReadKey();
}
}
Since it appears that you are wanting to restrict the types of values that can be contained, the nested TypedValueType class is marked protected and the ValueType constructor is marked private. Implicit operators are used to provide the "factory" logic for producing the appropriate typed TypeValueType subclasses for the values that are to be casted.
Here is the output from executing this as a console app:
1 - Decimal
Test - String
True - Boolean
10.1 - Decimal
5.12 - Decimal
Test2 - String
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;}
}
I am creating an Interface that will be implemented by classes which will be used as filters to create a query string. Each filter will be be responsible fr constructing a parameter in the query. The user can define whether to include the operator as well as what the operator shoudl be as well as the value. How the value is expressed depends on the datatype.
Boolean: " = 0"
Integer: " >= 2"
DateTime: " <> '2012-01-10'"
String: " = 'some string'"
public interface IFilter<T>
{
Nullable<T> Value { get; set; }
String Operator { get; set; }
Boolean IncludeOperator { get; set; }
}
This interface will be implemented by an abstract class which defines base fields, properties and overrides the ToString method; as well as an abstract GetValueAsString() method which shoudl be overriden to perform whatever logic required to contruct an appropriate string from the filter.
public abstract class Filter<T> : IFilter<T>
{
Nullable<T> _value;
String _operator = "=";
Boolean _includeOperator = false;
public Nullable<T> Value { get { return this._value; } set { this._value = value; } }
public String Operator { get { return this._operator; } set { this._operator = value; } }
public Boolean IncludeOperator { get { return this._includeOperator; } set { this._includeOperator = value; } }
public override string ToString()
{
String param = GetValueAsString();
if (param != null && this.IncludeOperator)
return this.Operator + " " + param;
else if (param != null && !this.IncludeOperator)
return param.Trim();
else
return null;
}
protected abstract String GetValueAsString();
}
I want to create typed filters derived from the interface and abstract class. Each filter will know how to convert a specific type into a string for inclusion in the query but each needs to adhere to the IFilter interface which it can do through it's inheritance of the Filter base class.
public class IntFilter: Filter<Int64>
{
protected override string GetValueAsString()
{
// Convert the integer to a string
return this.Value.Value.ToString();
}
}
public class BooleanFilter : Filter<Boolean>
{
protected override string GetValueAsString()
{
// convert the Boolean to a BIT value 1=True / 0=False
return (this.Value.HasValue && this.Value.Value) ? "1" : "0";
}
}
public class DateTimeFilter : Filter<DateTime>
{
protected override string GetValueAsString()
{
// Convert the DateTime to a dateTime string in the following format - 2/27/2009 12:12:22 PM
return (this.Value.HasValue) ? this.Value.Value.ToString("G") : null;
}
}
Here's where it gets tricky ... I need to create a filter for a string type but using the same Interface and abstract class.
public class StringFilter : Filter<String>
{
protected override string GetValueAsString()
{
// Convert the integer to a string
return this.Value.Value.ToString();
}
}
I need to be able to pass any of these filters into the following method ...
ExecuteQuery( IFilter filter );
You can see that there is a problem using a generic type for both a Reference and Value type. Does anyone have any idea how I can use this same interface for ALL the typed Filter classes? Is this possible?
You could pull the Nullable<> out of the definitions and then pass them into the implementations. Here is an example:
public interface IFilter<T>
{
T Value { get; set; }
String Operator { get; set; }
Boolean IncludeOperator { get; set; }
}
public abstract class Filter<T> : IFilter<T>
{
T _value;
String _operator = "=";
Boolean _includeOperator = false;
public T Value { get { return this._value; } set { this._value = value; } }
public String Operator { get { return this._operator; } set { this._operator = value; } }
public Boolean IncludeOperator { get { return this._includeOperator; } set { this._includeOperator = value; } }
public override string ToString()
{
String param = GetValueAsString();
if (param != null && this.IncludeOperator)
return this.Operator + " " + param;
else if (param != null && !this.IncludeOperator)
return param.Trim();
else
return null;
}
protected abstract String GetValueAsString();
}
public class IntFilter : Filter<Nullable<Int64>>
{
protected override string GetValueAsString()
{
// Convert the integer to a string
return this.Value.HasValue ? this.Value.ToString() : "0";
}
}
public class BooleanFilter : Filter<Nullable<Boolean>>
{
protected override string GetValueAsString()
{
// convert the Boolean to a BIT value 1=True / 0=False
return (this.Value.HasValue && this.Value.Value) ? "1" : "0";
}
}
public class DateTimeFilter : Filter<Nullable<DateTime>>
{
protected override string GetValueAsString()
{
// Convert the DateTime to a dateTime string in the following format - 2/27/2009 12:12:22 PM
return (this.Value.HasValue) ? this.Value.Value.ToString("G") : null;
}
}
public class StringFilter : Filter<String>
{
protected override string GetValueAsString()
{
return string.IsNullOrWhiteSpace(Value) ? "" : Value;
}
}