Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I cast an int value to a generic type parameter that is equivalent such as char?

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!

like image 368
user1730118 Avatar asked Nov 23 '12 20:11

user1730118


2 Answers

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>

like image 126
Radim Köhler Avatar answered Oct 01 '22 13:10

Radim Köhler


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.

like image 29
Mike Zboray Avatar answered Oct 01 '22 12:10

Mike Zboray