Top generics Questions

List of Tags
454
Ron Tuffin

I have an array that is initialised like:

Element[] array = {new Element(1),new Element(2),new Element(3)};

I would like to convert this array into an object of the ArrayList class.

ArrayList<Element> arraylist = ???;

I am sure I have done this before, but the solution is sitting just at the edge of my memory.

Answered By: Tom ( 564)
new ArrayList<Element>(Arrays.asList(array))
221
johnc

I'm building a function to extend the Enum.Parse concept that

  • Allows a default value to be parsed in case that an Enum value is not found
  • Is case insensitive

So I wrote the following:

public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
    if (string.IsNullOrEmpty(value)) return defaultValue;
    foreach (T item in Enum.GetValues(typeof(T)))
    {
        if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
    }
    return defaultValue;
}

I am getting a Error Constraint cannot be special class 'System.Enum'.

Fair enough, but is there a workaround to allow a Generic Enum, or am I going to have to mimic the Parse function and pass a type as an attribute, which forces the ugly boxing requirement to your code.

EDIT All suggestions below have been greatly appreciated, thanks.

Have settled on (I've left the loop to maintain case insensitivity - I am usng this when parsing XML)

public static class EnumUtils
{
    public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
        if (string.IsNullOrEmpty(value)) return defaultValue;

        foreach (T item in Enum.GetValues(typeof(T)))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}
Answered By: Vivek ( 194)

Since Enum Type implements IConvertible interface, a better implementation should be something like this:

public T GetEnumFromString<T>(string value) where T : struct, IConvertible
{
   if (!typeof(T).IsEnum) 
   {
      throw new ArgumentException("T must be an enumerated type");
   }

   //...
}

This will still permit passing of value types implementing IConvertible. The chances are rare though.

168
SaaS Developer

I'm using C# on Framework 3.5. I'm looking to quickly sort a Generic List<>. For the sake of this example lets say I have a List of a Person type with a property of lastname. How would I sort this List using a lambda expression?

List<Person> people = PopulateList();
people.OrderBy(???? => ?????)
Answered By: Marc Gravell ( 271)

If you mean an in-place sort (i.e. the list is updated):

people.Sort((x, y) => string.Compare(x.LastName, y.LastName));

If you mean a new list:

var newList = people.OrderBy(x=>x.LastName).ToList(); // ToList optional
150
Bryan Watts

This causes a compile-time exception:

public sealed class ValidatesAttribute<T> : Attribute
{

}

[Validates<string>]
public static class StringValidation
{

}

I realize C# does not support generic attributes. However, after much Googling, I can't seem to find the reason.

Does anyone know why generic types cannot derive from Attribute? Any theories?

Answered By: Jon Skeet ( 103)

Well, I can't answer why it's not available, but I can confirm that it's not a CLI issue. The CLI spec doesn't mention it (as far as I can see) and if you use IL directly you can create a generic attribute. The part of the C# 3 spec that bans it - section 10.1.4 "Class base specification" doesn't give any justification.

The annotated ECMA C# 2 spec doesn't give any helpful information either, although it does provide an example of what's not allowed.

My copy of the annotated C# 3 spec should arrive tomorrow... I'll see if that gives any more information. Anyway, it's definitely a language decision rather than a runtime one.

EDIT: Answer from Eric Lippert (paraphrased): no particular reason, except to avoid complexity in both the language and compiler for a use case which doesn't add much value.

145
pek

I mostly use Java and generics are relatively new. I keep reading that Java made the wrong decision or that .NET has better implementations etc. etc.

So, what are the main differences between C++, C#, Java in generics? Pros/cons of each?

Answered By: Orion Edwards ( 267)

I'll add my voice to the noise and take a stab at making things clear:

C# Generics allow you to declare something like this.

List<Person> foo = new List<Person>();

and then the compiler will prevent you from putting things that aren't Person into the list.
Behind the scenes the C# compiler is just putting List<Person> into the .NET dll file, but at runtime the JIT compiler goes and builds a new set of code, as if you had written a special list class just for containing people - something like ListOfPerson.

The benefit of this is that it makes it really fast. There's no casting or any other stuff, and because the dll contains the information that this is a List of Person, other code that looks at it later on using reflection can tell that it contains Person objects (so you get intellisense and so on).

The downside of this is that old C# 1.0 and 1.1 code (before they added generics) doesn't understand these new List<something>, so you have to manually convert things back to plain old List to interoperate with them. This is not that big of a problem, because C# 2.0 binary code is not backwards compatible. The only time this will ever happen is if you're upgrading some old C# 1.0/1.1 code to C# 2.0

Java Generics allow you to declare something like this.

ArrayList<Person> foo = new ArrayList<Person>();

On the surface it looks the same, and it sort-of is. The compiler will also prevent you from putting things that aren't Person into the list.

The difference is what happens behind the scenes. Unlike C#, Java does not go and build a special ListOfPerson - it just uses the plain old ArrayList which has always been in Java. When you get things out of the array, the usual Person p = (Person)foo.get(1); casting-dance still has to be done. The compiler is saving you the key-presses, but the speed hit/casting is still incurred just like it always was.
When people mention "Type Erasure" this is what they're talking about. The compiler inserts the casts for you, and then 'erases' the fact that it's meant to be a list of Person not just Object

The benefit of this approach is that old code which doesn't understand generics doesn't have to care. It's still dealing with the same old ArrayList as it always has. This is more important in the java world because they wanted to support compiling code using Java 5 with generics, and having it run on old 1.4 or previous JVM's, which microsoft deliberately decided not to bother with.

The downside is the speed hit I mentioned previously, and also because there is no ListOfPerson pseudo-class or anything like that going into the .class files, code that looks at it later on (with reflection, or if you pull it out of another collection where it's been converted into Object or so on) can't tell in any way that it's meant to be a list containing only Person and not just any other array list.

C++ Templates allow you to declare something like this

std::list<Person>* foo = new std::list<Person>();

It looks like C# and Java generics, and it will do what you think it should do, but behind the scenes different things are happening.

It has the most in common with C# generics in that it builds special pseudo-classes rather than just throwing the type information away like java does, but it's a whole different kettle of fish.

Both C# and Java produce output which is designed for virtual machines. If you write some code which has a Person class in it, in both cases some information about a Person class will go into the .dll or .class file, and the JVM/CLR will do stuff with this.

C++ produces raw x86 binary code. Everything is not an object, and there's no underlying virtual machine which needs to know about a Person class. There's no boxing or unboxing, and functions don't have to belong to classes, or indeed anything.

Because of this, the C++ compiler places no restrictions on what you can do with templates - basically any code you could write manually, you can get templates to write for you.
The most obvious example is adding things:

In C# and Java, the generics system needs to know what methods are available for a class, and it needs to pass this down to the virtual machine. The only way to tell it this is by either hard-coding the actual class in, or using interfaces. For example:

int addNames<T>( T first, T second ) { return first.Name() + second.Name(); }

That code won't compile in C# or Java, because it doesn't know that the type T actually provides a method called Name(). You have to tell it - in C# like this:

interface IHasName{ string Name(); };
int addNames<T>( T first, T second ) where T : IHasName { .... }

And then you have to make sure the things you pass to addNames implement the IHasName interface and so on. The java syntax is different (<T extends IHasName>), but it suffers from the same problems.

The 'classic' case for this problem is trying to write a function which does this

int addNames<T>( T first, T second ) { return first + second; }

You can't actually write this code because there are no ways to declare an interface with the + method in it. You fail.

C++ suffers from none of these problems. The compiler doesn't care about passing types down to any VM's - if both your objects have a .Name() function, it will compile. If they don't, it won't. Simple.

So, there you have it :-)

143
climatewarrior

Due to the implementation of Java Generics you can't have code like this. How can I implement this while maintaining type safety?

public class GenSet<E> {
    private E a[];
    public GenSet()
    {
        a = new E[INITIAL_ARRAY_LENGTH];
    }
}

I saw a solution on the java forums that goes like this:

import java.lang.reflect.Array;

class Stack<T> {
  public Stack(Class<T> clazz,int capacity) {
     array=(T[])Array.newInstance(clazz,capacity);
  }

  private final T[] array;
}

But I really don't get what's going on. Can anyone help? Thanks in advance.

Answered By: Varkhan ( 101)

I have to ask a question in return: is your GenSet "checked" or "unchecked"? What does that mean?

  • checked: strong typing. GenSet knows explicitly what type of objects it contains (i.e. its constructor was explicitly called with a Class argument, and methods will throw an exception when they are passed arguments that are not of type E. See Collections.checkedCollection(java.util.Collection, java.lang.Class)

    -> in that case, you should write

    private E[] a
    
    @SuppressWarnings({"unchecked"})
    public GenSet(Class<E> c, int s) {
        // Use Array native method to create array of a type only known at run time
        a = (E[]) Array.newInstance(c,s);
    }
    
    E get(int i) {
        return a[i];
    }
    
  • unchecked: weak typing. No type checking is actually done on any of the objects passed as argument.

    -> in that case, you should write

    private Object[] a
    
    
    public GenSet(int s) {
        a = new Object[s];
    }
    
    @SuppressWarnings({"unchecked"})
    E get(int i) {
        return (E) a[i];
    }
    

All of this results from a known, and deliberate, weakness of generics in Java: it was implemented using erasure, so "generic" classes don't know what type argument they were created with at run time, and therefore can not provide type-safety unless some explicit mechanism (type-checking) is implemented.

142
Peanut

Can anyone explain to me why I would want to use IList over List in C#?

Related question: Why is it considered bad to expose List<T>

Answered By: tvanfosson ( 144)

If you are exposing your class through a library that others will use, you generally want to expose it via interfaces rather than concrete implementations. This will help if you decide to change the implementation of your class later to use a different concrete class. In that case the users of your library won't need to update their code since the interface doesn't change.

If you are just using it internally, you may not care so much, and using List<T> may be ok.