I'm new to generics and I'm having some trouble implementing a small self practice code.
I'm creating a linked list. I want it to store char or int values. So I decided to make the implementation generic:
public class Node<T> where T : struct, IConvertible
{
public Node<T> next = null;
public T data = default(T);
public Node(T value) { this.data = value; }
}
I have a method that creates the linked list by generating random values in the range [33,127), converting the value to the type specified by T (e.g. if 86 is generated and T is Char, then the value to be stored in the linked list node will be 'V'; If T is Int32, then value will simply be 86). I'm facing two problems:
static Node<IConvertible> CreateList<T>(int len) where T : struct, IConvertible
{
Random r = new Random((int)DateTime.Now.Ticks);
T value = (T)r.Next(33, 127); // Problem #1
Node<T> head = new Node<T>(value);
Node<T> n = head;
for (int i = 1; i < len; i++)
{
n.next = new Node<T>(value);
n = n.next;
}
return head; // Problem #2
}
These are the problems :
1) Normally this is possible: (int) value = (char) r.Next(33, 127). Why if T is of type Char, the compiler says "Cannot convert type 'int' to 'T'", even if I had specified "where T : struct, IConvertible"?
2) "Cannot implicitly convert type 'LinkedList.Node<T>
' to 'LinkedList.Node<System.IConvertible>
'" If T is either Int32 or Char and both of them implement IConvertible, what is the way to cast Node<Int32>
or Node<Char>
to Node<IConvertible>
?
Thanks a lot!
The issue is that T could be any struct, e.g. Guid, SByte... or custom-new-one. and while we can be sure that T is struct
and IConvertible
, there does not have to be explicit cast operator to
public static explicit operator AnyStruct(int i)
The second issue casting Node<System.IConvertible>
to Node<System.IConvertible>
is usual. Any list List<T>
cannot be converted to List<System.IConvertible>
.
What we need is covariance declaration on interface: INode<out T>
. Then the INode<T>
could be converted into INode<System.IConvertible>
Your use of Random
to generate some data is sort of at odds with using generics. I would separate the two like so:
static Node<T> CreateList<T>(int len, Func<T> dataProvider) where T : struct, IConvertible
{
Node<T> head = new Node<T>(dataProvider());
Node<T> n = head;
for (int i = 1; i < len; i++)
{
n.next = new Node<T>(dataProvider());
n = n.next;
}
return head;
}
calling code:
Random r = new Random();
Node<char> list = CreateList(10, () => (char)r.Next(33, 127));
The second issue is Node<IConvertible>
isn't allowed by your struct
constraint on Node<T>
. Just return a Node<T>
. Even if you do remove the struct
constraint from Node<T>
, it would not be possible to return a Node<IConvertible>
because generic classes do not support variance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With