is there anyway to unbox an object to its real type?
Basically I am given an ArrayList, the array list are actually a list of int or double, or maybe other types ( it can be either, but it is either all int or double, no mix). Now, I will have to return a List<double> or List<int> or other list, depending on what is the real type.
public List<T> ConvertToList<T>(ArrayList arr)
{
var list1 = new List<T>();
foreach(var obj in arr)
{
// how to do the conversion?
var objT = ??
list1.Add(objT);
}
return list1;
}
Any idea?
If you're using .NET 3.5, there's a much easier way to do this:
public List<T> ConvertToList<T>(IEnumerable original)
{
return original.Cast<T>().ToList();
}
(I've generalised the parameter to just IEnumerable as we're not using anything specific to ArrayList here.)
You can use the Convert class.
var objT = (T)Convert.ChangeType(obj, typeof(T));
You can just regular Type Cast like this
public List<T> ConvertToList<T>(ArrayList arr)
{
var list1 = new List<T>();
foreach(var obj in arr)
{
// Like this
list1.Add((T)obj);
}
return list1;
}
Related
I have a class A from which B and C inherit.
I have two lists: listB and listC, of the respective types.
I want to make a method that returns the two lists inside an array, like so:
public override List<A>[] GetAllItems()
{
return new List<A>[2]
{
listB,
listC
};
}
However, when I try this approach, I get the following error, because I try to convert the inherited types incorrectly.
Cannot implicitly convert type 'System.Collections.Generic.List<Lae.B>' to 'System.Collections.Generic.List<Lae.A>' [Assembly-CSharp]csharp(CS0029)
Is there some way to create this array without converting the elements?
Note: I am not using a struct or class, because I want the array to be of various sizes based on logic above.
public List<A>[] GetAllItems()
{
var result = new List<A>[2] {
listB.Cast<A>().ToList(),
listC.Cast<A>().ToList(),
};
return result;
}
If you need to return array of Lists - easiest way is to use Cast linq extension method.
In reference to the comments you have to remember that if you modify listB or listC, the change won't be reflected in the casted collections.
Anyway, if you need to have an access to the original listB / listC collections references, you can use IEnumerable instead of List in order to not be forced to "materialize" the results. Example:
public IEnumerable<A>[] GetAllItems()
{
return new IEnumerable<A>[] {
listB,
listC,
};
}
Now when you access eg. allItems[0] it will reference to the original listB collection.
You can't do that because instance of B is an also A but instance of List of B is not also List of A.
You should box the types in the collection, you can use Cast function of in the Linq namespace.
using System.Linq;
List<A>[] GetAllItems()
{
var result = new List<A>[2] {
listB.Cast<A>().ToList(),
listC.Cast<A>().ToList(),
};
return result;
}
Or you can do that manualy.
List<A>[] GetAllItems()
{
var boxedListB = new List<A>();
var boxedListC = new List<A>();
foreach (var item in listB)
{
boxedListB.Add(item);
}
foreach (var item in listC)
{
boxedListC.Add(item);
}
var result = new List<A>[2] {
boxedListB,
boxedListC
};
return result;
}
Or you can use Select function in System.Linq namespace.
List<A>[] GetAllItems()
{
var result = new List<A>[2] {
listB.Select(x=> x as A).ToList(),
listC.Select(x=>x as A).ToList()
};
return result;
}
You can check this document for more information about boxing/unboxing.
I'm trying to make a generic function that converts a enum System.Array into a List of those enums, I can not know the type of the enum array. I have tried several ways but I have not been able to make it work. It would be something like this..., Thanks
public static List<T> SArrayEnumToList<T>(System.Array arr){
Type enumType = typeof(T);
if(enumType.BaseType != typeof(Enum))
throw new ArgumentException("T must be of type System.Enum");
List<T> enumList = new List<T>(new T[arr.Length]);
int i;
for(i=0;i<arr.Length;i++) {
enumList.Add(( T )Enum.Parse(enumType, arr.GetValue(i).ToString()));
}
return enumList;
}
You only really need to use the Linq ToList() method:
var myEnumsList = myEnumsArray.ToList();
The documentation states that ToList() returns a list "that contains elements from the input sequence".
If you really want to break this functionality out to your own method, you may do this like following:
private static List<T> ToList<T>(T[] enums) where T : struct, IConvertible
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException("T must be an enum.");
}
return enums.ToList();
}
Constraining type of generic type T limits what types may be used when calling the method. Enum is a struct and implements IConvertible as discussed in here.
EDIT:
Since you really need to use System.Array. Iterate the System.Array, cast every value to generic type T and add to list before returning.
Working example:
public static List<T> ToList<T>(Array array) where T : struct, IConvertible
{
if (!typeof(T).IsEnum)
{
throw new ArgumentException("T must be an enum.");
}
List<T> enumValues = new List<T>();
foreach (var enumValue in array)
{
enumValues.Add((T)enumValue);
}
return enumValues;
}
EDIT #2
Update after comments.
public static IList ToList(Array array)
{
Type elementType = array.GetType().GetElementType();
Type listType = typeof(List<>).MakeGenericType(new[] { elementType });
IList list = (IList)Activator.CreateInstance(listType);
foreach (var enumValue in array)
{
list.Add(enumValue);
}
return list;
}
Assuming you have a array of int, this should work i guess
public static List<T> SArrayEnumToList<T>(int[] arr) where T : struct, IConvertible
{
if (!typeof (T).IsEnum)
throw new ArgumentException("T must be of type System.Enum");
// cast to object first
return arr.Cast<object>()
.Cast<T>()
.ToList();
}
// or
public enum Test
{
blah,
balh2,
blah3
}
...
var results = ((Test[])(object)values).ToList();
Full Demo Here
How do I fix this function so that I can input a List of any class
private static List<Type> CopyObjList(List<Type> Group, List<Type> AddGroup)
{
foreach (Type obj in AddGroup)
Group.Add(obj);
return Group;
}
Generics and AddRange
private static void CopyObjList<T>(List<T> Group, List<T> AddGroup)
{
Group.AddRange(AddGroup);
}
Return type is not mendatory since List is a reference type.
Have a nice day
As I can see, you just need to add a range of elements to an existing List.
You do not need to write the function for this. You can simply use
AddRange
Method of the List class
A couple of things to think of:
Generics
Do we want to be able to add derived types too
Do you really want to modify the existing list.
private static List<TList> CopyObjList<TList, TElem>(List<TList> Group, List<TElem> AddGroup)
where TElem : TList
{
List<TList> result = new List<TList>(Group);
foreach (var item in AddGroup)
{
result.Add(item);
}
return result;
}
This will leave the original list unchanged, return the new list, and also allow instances of derived classes to be added as well.
Generics.
private static List<T> CopyObjList<T>(List<T> Group, List<T> AddGroup)
{
foreach(T obj in AddGroup)
Group.Add(obj);
return Group;
}
This allows you to pass a compile time type into the function.
Example:
List<string> employees = new List<string> { "Bob", "Joe", "Fred" };
List<string> newHires = new List<string> { "Steve", "John" };
List<string> allEmployees = CopyObjList<string>(employees, newHires);
foreach(string employeeName in allEmployees)
Console.WriteLine(employeeName);
Not sure about your question, but it seems like you need a collection that accepts any type. If so, an ArrayList can do the job:
private static ArrayList CopyObjList(ArrayList Group, ArrayList AddGroup)
{
ArrayList arr = new ArrayList(Group);
arr.AddRange(AddGroup);
return arr;
}
If the group to add may be different from ArrayList, then you can have ICollection for it:
private static ArrayList CopyObjList(ArrayList Group, ICollection AddGroup)
{
ArrayList arr = new ArrayList(Group);
arr.AddRange(AddGroup);
return arr;
}
I would like to create a function that will return list of type that is specified by me at run time.
I tried something along this line:
public static List<T> GetMyList<T>(string[] itemList)
{
List<T> resultList = new List<T>(itemList.Length);
return resultList.AddRange(itemList);
}
But this doesn't work. Obviously I don't fully understand how to pass a type to be converted to.
Any help would be appreciated it.
Edit:
It looks like that it is not possible, but here is more info. String array will contain numbers and I would like to convert those numbers sometimes into int, sometimes into short.
Idea behind is to have a generic function that will attempt to convert items into whatever type list I tell it.
You need to provide a method to convert a string into a T - you can do this using a Func<string, T>:
public static List<T> GetMyList<T>(string[] itemList, Func<string, T> conversionFunc)
{
return itemList.Select(conversionFunc).ToList();
}
e.g.
List<int> ints = GetMyList(new[] { "1", "2", "3" }, s => int.Parse(s));
A slightly more elegant solution would be to add an extension method to string that automatically calls the parser for type T, like so:
public static class GenericParser {
public static T Parse<T>(this string input) {
var converter = TypeDescriptor.GetConverter(typeof(T));
if ( converter != null ) {
return ( T )converter.ConvertFromString(input);
}
return default(T);
}
}
Then, your conversion function would look something like:
public static List<T> GetMyList<T>(string[] itemList) {
List<T> list = new List<T>();
list.AddRange(Array.ConvertAll<string, T>(itemList, delegate(string s) {
return s.Parse<T>();
}));
return list;
}
And the usage would be:
List<int> integers = GetMyList<int>(new []{"1", "2", "3", "4"});
List<double> doubles = GetMyList<double>(new []{"1.0", "2.0", "3.0", "4.0"});
and so on.
My first thought is that this won't work because not every object type can be constructed from a string. Perhaps you want something with a signature more like:
public static List<T> GetMyList<T>(T[] itemList)
{
List resultList = new List(itemList.Length);
foreach (t in itemList)
{
resultList.add(t);
}
return resultList;
}
(forgive my syntax. I don't have a compiler handy right now to check it.)
this doesn't work because system has no idea how to convert string to generic T. Also even if it is known, it will not work, because C# (prior to 4) doesn't have type covariance. So use either foreach to copy and convert elements one by one or use Select from Linq
Similar to Lee's but more generic...
public static class Tools
{
public static List<TResult> ToList<T, TResult>(
this IEnumerable<T> input,
Func<T, TResult> conversion)
{
return input.Select(conversion).ToList();
}
public static List<TResult> ToList<T, TResult>(
this IEnumerable<T> input,
Func<T, int, TResult> conversion)
{
return input.Select(conversion).ToList();
}
}
class Program
{
static void Main(string[] args)
{
var input = new[] { "1", "2", "3" };
var ret = input.ToList(i => int.Parse(i));
// 1,2,3
var ret2 = input.ToList((i,j) => int.Parse(i) + j * 10);
// 1,12,23
}
}
I'm trying to perform the following cast
private void MyMethod(object myObject)
{
if(myObject is IEnumerable)
{
List<object> collection = (List<object>)myObject;
... do something
}
else
{
... do something
}
}
But I always end up with the following excepction:
Unable to cast object of type 'System.Collections.Generic.List1[MySpecificType]' to type 'System.Collections.Generic.List1[System.Object]'
I really need this to work because this method needs to be very generic to receive single objects and collections both of unspecified types.
Is this possible, or is there another way of accomplishing this.
Thank you.
C# 4 will have covariant and contravariant template parameters, but until then you have to do something nongeneric like
IList collection = (IList)myObject;
You can't cast an IEnumerable<T> to a List<T>.
But you can accomplish this using LINQ:
var result = ((IEnumerable)myObject).Cast<object>().ToList();
Problem is, you're trying to upcast to a richer object. You simply need to add the items to a new list:
if (myObject is IEnumerable)
{
List<object> list = new List<object>();
var enumerator = ((IEnumerable) myObject).GetEnumerator();
while (enumerator.MoveNext())
{
list.Add(enumerator.Current);
}
}
This Code worked for me
List<Object> collection = new List<Object>((IEnumerable<Object>)myObject);
Do you actually need more information than plain IEnumerable gives you? Just cast it to that and use foreach with it. I face exactly the same situation in some bits of Protocol Buffers, and I've found that casting to IEnumerable (or IList to access it like a list) works very well.
How about
List<object> collection = new List<object>((IEnumerable)myObject);
Have to join the fun...
private void TestBench()
{
// An object to test
string[] stringEnumerable = new string[] { "Easy", "as", "Pi" };
ObjectListFromUnknown(stringEnumerable);
}
private void ObjectListFromUnknown(object o)
{
if (typeof(IEnumerable<object>).IsAssignableFrom(o.GetType()))
{
List<object> listO = ((IEnumerable<object>)o).ToList();
// Test it
foreach (var v in listO)
{
Console.WriteLine(v);
}
}
}
Nowadays it's like:
var collection = new List<object>(objectVar);
You need to type cast using as operator like this.
private void MyMethod(object myObject)
{
if(myObject is IEnumerable)
{
List<object> collection = myObject as(List<object>);
... do something
}
else
{
... do something
}
}
You could also convert object to a list of your DTO.
First serialize myObject and then deserilize it into your List.
Here is the code:
List<MyDto> myList = JsonConvert.DeserializeObject<List<MyDto>>(JsonConvert.SerializeObject(myObject));
The Newtonsoft.Json is used in the above code.