tags:

views:

182

answers:

8

My friend is trying to create a utility function that is given some Type and in that function it creates a generic List of that type. We're having trouble creating that list:

public static List<T> GetQueryResult(string xpathQuery, Type itemType) {

  // this line does not work:
  List<itemType> lst = new List<itemType>();

  return lst;
}

Are there any easy solutions to this?

UPDATE:

Is there any way to basically do this???

List<T> lst = new List<T>(); 
foreach (Sitecore.Data.Items.Item i in items) { 
  lst.Add(new T(i)); 
}
A: 

It is possible using reflection, for example:

var type = typeof(int); // var type = itemType : put this line to fit the method
var genericListType = typeof(List<>).MakeGenericType(type);
var genericList = Activator.CreateInstance(genericListType);

Assert.IsTrue(genericList is List<int>);

In your example, ehere do you get T from that you use in the return type? Maybe there is no need to use here reflection.

If you do not get T as generic argument then you cannot return the List as generic List and the method will have to return a non generic type (like IList instead of List).

Elisha
No reason to go through all this hassle...the generic type is passed in and can be used directly, as referenced in other posts.
jvenema
-1 While possible in isolation, you can't insert that code snippet in the GetQueryResult method, since the compile-time type of genericList is object, not List<T>. With the signature given, the GetQueryResult method doesn't compile, and your answer can't change that.
Mark Seemann
+20  A: 
public static List<T> GetQueryResult<T>(string xpathQuery/*, Type itemType you don't need this because you specified the Type with T*/) {

  // this line works now:
  List<T> lst = new List<T>();

  return lst;
}

Then you would call the method like so:

List<int> results = GetQueryResult<int>("xpathQuery");

Edit:

Are you wanting to do something like this?

List<YourType> lst = items.Select<Sitecore.Data.Items.Item, YourType>(
siteCoreItem => new YourType()
{
   PropertyA = siteCoreItem.PropertyA,
}
);

If YourType inherrits from Sitecore.Data.Items.Item you can use Cast:

List<YourType> list = items.Cast<YourType>();
J.13.L
I kind of got the impression that the OP doesn't know the type at compile time, in which case this isn't going to help...
Mark Seemann
Thank you everyone for the upvotes...
J.13.L
A: 

Generic type arguments are resolved compile time, so to have the code working you'd need to pass itemType as a type argument or change the return type to IList And Them use the solution given by ELisha but that would mean loosing type information on the Call site

Rune FS
+1  A: 

While Elisha's answer shows you how you can create a constructed generic type from a Type instance, it's not going to help you because what I think you want to do is not possible: the signature of the GetQueryResult method is illegal because T is unspecified (unless the method is a member of a generic type itself).

The method will not compile as given.

If you already know the type, you can change it to

public static List<T> GetQueryResult<T>(string xpathQuery)
{
    var lst = new List<T>();
    return lst;
}

but that's probably not what you want...

Mark Seemann
+3  A: 

Define that method like this:

    public static List<T> GetQueryResult<T>(string xpathQuery)
    {
        List<T> lst = new List<T>();

        // do stuff

        return lst;
    }

and call it like this:

 List<SomeType> items = SomeClass.GetQueryResult<SomeType>("query");
mdm20
Ok, so in the `// do stuff` how do I create an object of T `T`?
Mark Ursino
T obj = default(T);
mdm20
No (unless T is a value type), default(T) is null, not a new instance of T
erikkallen
`T obj = new T();`You would also want to use the `new()` constraint to require that `T` has a parameterless constructor:`public static List<T> GetQueryResult<T>(string xpathQuery) where T : new() {...}`
Annabelle
A: 
public static List<T> GetQueryResult<T>(string xpathQuery) {
    List<T> lst = new List<T>();
    return lst;
}

is the only way if you want static typing. Otherwise you could do

public static IList GetQueryResults(string xpathQuery, Type itemType) {
    Type tp = typeof(List<>).MakeGenericType(itemType);
    IList lst = (IList)Activator.CreateInstance(tp);
    return lst;
}

but using a non-generic list would probably be better in that case.

Edit: You asked another question in the same post:

The 3 ways of creating an instance of a generic type are

  1. use the where T : new() constraint and use the default constructor (doesn't seem good enough for you).
  2. Use reflection. Rarely the best idea.
  3. Specify a creator function

like this:

public static List<T> GetQueryResults<T>(string xpathQuery, Func<int, T> creator) {
    var result = new List<T>();
    foreach (i in something)
        result.add(creator(i));
    return result;
}

and then invoke it like:

List<int> l = GetQueryResults("something", i => new MyObject(i));
erikkallen
itemType not defined.
recursive
Typo fixed.....
erikkallen
A: 

Answer to the updated question:

public List<T> GetQueryResult<T>(string xPathQuery)
{
    var items = ;// logic to get items
    var list = new List<T>();
    foreach (Sitecore.Data.Items.Item item in items)
    {
        list.Add((T) Activator.CreateInstance(typeof(T), item));
    }

    return list;
}

I assume that T has a constructor that gets Sitecore.Data.Items.Item, if it won't have the code will fail at runtime.

There must be a safer way to do it, it'll be better if you can give wider context to the problem.

Elisha
A: 

As others have demonstrated, the only way to solve your updated question for any T is with reflection. However, if T is restricted to a well known set of types that you can modify, you could do this:

public interface IItemContainer
{
  void SetItem(Sitecore.Data.Items.Item item);
}

public static List<T> GetQueryResult<T>(string xpathQuery)
  where T : IItemContainer, new() {
  IList<Sitecore.Data.Items.Item> items = GetAListOfItemsSomehow(xpathQuery);

  List<T> result = new List<T>();
  foreach (Sitecore.Data.Items.Item item in items) {
    T obj = new T();
    obj.SetItem(item);
    result.add(obj);
  }

  return result;
}

Any types you want to use for T would then have to implement IItemContainer.

Annabelle