Difference between Java Generics and C# Generics

As you may already know, Java's generics are just compile-time checking and are not implemented in the JVM, whereas C#'s generics have type-checking implemented in both the compiler and the CLR and is more strongly-typed. However, Java's looser implementation allows the following, whereas C#'s does not:

Lets say we have a generic interface:

public interface GenericInterface<T> {
    public int getSize(T t);
}

And we have interfaces that extend this interface:

public interface StringInterface extends GenericInterface<String> {
}

public interface IntegerInterface extends GenericInterface<Integer> {
}

And classes that implement those interfaces:

public class StringImplementation implements StringInterface {
    public int getSize(String t) {
        return t == null ? 0 : t.length();
    }
}

public class IntegerImplementation implements IntegerInterface {
    public int getSize(Integer t) {
        return t == null ? 0 : t.intValue();
    }
}

The following is possible in Java, written in the form of a JUnit test:

public class Tests extends TestCase {
    @SuppressWarnings("unchecked")
    public int callGetSize(GenericInterface g, Object o) {
        return g.getSize(o);        
    }
    
    public void testCallDoSomething() {        
        assertEquals(4, callGetSize(new StringImplementation(), "test"));
        assertEquals(10, callGetSize(new IntegerImplementation(), Integer.valueOf(10)));
    }
    
}

When used without specificying the type constraints, GenericInterface basically acts like GenericInterface<Object>. This is rather loose typing, and could lead to runtime errors. But when used correctly, it is very flexible and is useful when you are writing very generic code (using generics for their templating ability more than type safety).

In C#, this is not possible, because it does not allow you to have an unspecified generic. You cannot even use something like GenericInterface<Object> to do the same thing. For an explanation and further discussion of this, see a blog entry by Rick Byers, who is a developer on the CLR team at Microsoft.

We can workaround this situation by creating a delegate. For example, lets say we re-implemented this in C# like so:

    public interface GenericInterface<T> {
        int GetSize(T t);
    }

    public interface IntegerInterface : GenericInterface<int?> {
    }

    public interface StringInterface : GenericInterface<string> {
    }

    public class StringImplementation : StringInterface {
        public int GetSize(string t) {
            return t == null ? 0 : t.Length;
        }
    }

    public class IntegerImplementation : IntegerInterface {
        public int GetSize(int? t) {
            return t.HasValue ? t.Value : 0;
        }
    }

We can write a delegate class that looks like this:

    public class ObjectDelegateImplementation<T> : GenericInterface<object> {

        private GenericInterface<T> g;

        public ObjectDelegateImplementation(GenericInterface<T> g) {
            this.g = g;
        }

        public int GetSize(object t) {
            return g.GetSize((T)t);
        }

    }

And we can use the delegate like this:

    public class Tests {
        public int CallGetSize(GenericInterface<object> g, Object o) {
            return g.GetSize(o);
        }

        public void testCallDoSomething() {
            int ret1 = CallGetSize(
                new ObjectDelegateImplementation<string>(new StringImplementation()),
                "test");
            int ret2 = CallGetSize(
                new ObjectDelegateImplementation<int?>(new IntegerImplementation()),
                10);
            Console.WriteLine(ret1);
            Console.WriteLine(ret2);
        }
    }