Reputation: 37123
I am writing an application for serialization of a generic List that holds any datatype.So I designed a base-DataType to fill the list with as follows:
public abstract class GenericObject<T> {
public string key;
public T value;
public GenericObject() { }
public GenericObject(string key, T value) : this() {
this.key = key;
this.value = value;
}
}
Furthermore there is a class GenericList
which implements the IXmlSerializable
-Interface to write a key-value pair as like this:
GenericObject<int> myInt = new GenericObject<int>("key", 3);
Which will produce the following XML:
<key>3</key>
The class-definition for the GenericList
is more or less as follows:
public class GenericList<T> : IXmlSerializable {
List<T> objects;
// ...
}
So let´s assume we have a class Person
that derives from GenericObject<string>
(no matter how it looks like) and we want to fill the list with a couple of persons. The problem I have is defining a constraint on the generic type T of the GenericList
-class so that only types are possible that derive from GenericObject
. I already tried it by using public class GenericList<T> : IXmlSerializable where T : GenericObject<object>
but that did not work because of the following compiler error:
Person' cannot be used as type parameter 'T' in the generic type or method 'GenericList<T>'. There is no implicit reference conversion from 'Person' to 'GenericObject<object>'
I also tried out leaving the where-clause empty, but then I have to check the type of T
within the GenericList
in order to get its key and value where I also failed with the following:
if (typeof(T).IsAssignableFrom(typeof(GenericObject<object>)))
which will always return false as T
is of type Person and not GenericObject<object>
.
May anyone have some suggestion how I can fill my list with a person?
Upvotes: 0
Views: 12393
Reputation: 2709
You can use covariance. Since variant type parameters only can be declared in interfaces and delegates (not in class definitions), you also need to define an interface:
public interface IGenericObject<out T>
{
string Key { get; }
T Value { get; }
}
public abstract class GenericObject<T> : IGenericObject<T>
{
public string Key { get; set; }
public T Value { get; set; }
protected GenericObject() { }
protected GenericObject(string key, T value)
: this()
{
this.Key = key;
this.Value = value;
}
}
public class GenericList<TGenericObject> : IXmlSerializable
where TGenericObject : IGenericObject<object>
{
private readonly List<TGenericObject> _list = new List<TGenericObject>();
public void Add(TGenericObject item)
{
_list.Add(item);
}
public XmlSchema GetSchema()
{
// ...
}
public void ReadXml(XmlReader reader)
{
// ...
}
public void WriteXml(XmlWriter writer)
{
// ...
}
}
public class Person : GenericObject<string>
{
}
Now you can do this:
public class SomeClass
{
public void SomeMethod()
{
Person somePerson = new Person();
GenericList<IGenericObject<object>> listWithGenericsOfObject = new GenericList<IGenericObject<object>>();
listWithGenericsOfObject.Add(somePerson);
GenericList<IGenericObject<string>> listWithGenericsOfString = new GenericList<IGenericObject<string>>();
listWithGenericsOfString.Add(somePerson);
GenericList<Person> listWithGenericsOfPerson = new GenericList<Person>();
listWithGenericsOfPerson.Add(somePerson);
}
}
Now you don't need to check the type at runtime using IsAssignableFrom. However, in case you will need it, you should swap the types like this:
typeof(GenericObject<object>).IsAssignableFrom(typeof(T))
Upvotes: 5