Convert IList to SomeType[] - c#

I have a collection of type List that I want to convert to SomeType[]. SomeType is not known before runtime.
This must be done with the signature of the following procedure.
private object ConvertListToArray(IList collection)
{
// This does not work since SomeType is not known before runtime.
var convertedList = collection.Cast<SomeType>().ToArray();
return convertedList;
}
Notice that collection is IList, but it is known that the concrete type is
List<SomeType>
The return collection must be an object of type SomeType[].
How can this be done?

public static class ListExtensions
{
public static T[] ConvertToArray<T>(IList list)
{
return list.Cast<T>().ToArray();
}
public static object[] ConvertToArrayRuntime(IList list, Type elementType)
{
var convertMethod = typeof(ListExtensions).GetMethod("ConvertToArray", BindingFlags.Static | BindingFlags.Public, null, new [] { typeof(IList)}, null);
var genericMethod = convertMethod.MakeGenericMethod(elementType);
return (object[])genericMethod.Invoke(null, new object[] {list});
}
}
[TestFixture]
public class ExtensionTest
{
[Test]
public void TestThing()
{
IList list = new List<string>();
list.Add("hello");
list.Add("world");
var myArray = ListExtensions.ConvertToArrayRuntime(list, typeof (string));
Assert.IsTrue(myArray is string[]);
}
}

You can do this with the implementation below:
class Program
{
static void Main(string[] args)
{
// same type
var myCollection = new List<string> {"Hello", "World"};
var array = (string[])myCollection.ConvertToArray();
Console.WriteLine(array[0]);
// new type
var intList = new List<int> {1, 2, 3};
var stringArray = (string[])intList.ConvertToArray(typeof(string));
Console.WriteLine(stringArray[0]);
// mixed types
var ouch = new List<object> {1, "Mamma", 3.0};
var result= (string[])ouch.ConvertToArray(typeof(string));
Console.WriteLine(result[0]);
}
}
The implementation:
public static class ListExtensions
{
public static object ConvertToArray(this IList collection)
{
// guess type
Type type;
if (collection.GetType().IsGenericType && collection.GetType().GetGenericArguments().Length == 0)
type = collection.GetType().GetGenericArguments()[0];
else if (collection.Count > 0)
type = collection[0].GetType();
else
throw new NotSupportedException("Failed to identify collection type for: " + collection.GetType());
var array = (object[])Array.CreateInstance(type, collection.Count);
for (int i = 0; i < array.Length; ++i)
array[i] = collection[i];
return array;
}
public static object ConvertToArray(this IList collection, Type arrayType)
{
var array = (object[])Array.CreateInstance(arrayType, collection.Count);
for (int i = 0; i < array.Length; ++i)
{
var obj = collection[i];
// if it's not castable, try to convert it
if (!arrayType.IsInstanceOfType(obj))
obj = Convert.ChangeType(obj, arrayType);
array[i] = obj;
}
return array;
}
}

You could try:
private T[] ConvertListToArray<T>(IList collection)
{
// This does not work since SomeType is not known before runtime.
var convertedList = collection.Cast<T>().ToArray();
return convertedList;
}

I'm posting this as the other answers seem a bit verbose. I'm pretty sure this does what the OP is looking for. It worked for me.
public static Array ConvertToArray(ICollection collection, Type type)
{
var array = Array.CreateInstance(type, collection.Count);
collection.CopyTo(array, 0);
return array;
}

Related

Linq Select dynamically from generic <T> List

I want to get a list of each object from my List<T> (except strings, ints etc). And then Invoke (generic, recursive method with reflection). The problem is I am iterating on the property names, and have no idea how to select.
Error CS0021 Cannot apply indexing with [] to an expression of type 'T'
Code:
public static void My method<T>(IEnumerable<T> query)
{
var t = typeof(T);
var Headings = t.GetProperties();
for (int i = iteratorStart; i < Headings.Count(); i++)
{
if (IsValue(Headings[i].PropertyType.FullName))
{
}
else
{
Type type = Type.GetType(Headings[i].PropertyType.FullName);
var mi = typeof(ExcelExtension);
var met = mi.GetMethod("ListToExcel");
var genMet = met.MakeGenericMethod(type);
var nested = query.Select(p => p[Headings[i].Name]);
object[] parametersArray = new object[] { pck, nested, i };
genMet.Invoke(null, parametersArray);
}
}
}
As far as I can see, this is what you want:
public static void Mymethod<T>(IEnumerable<T> query)
{
var t = typeof(T);
int pck = 1234;
var mi = typeof(ExcelExtension);
var met = mi.GetMethod("ListToExcel");
var Headings = t.GetProperties();
for(int i=0; i < Headings.Length; ++i)
{
var prop = Headings[i];
if (prop.PropertyType.IsClass)
{
var genMet = met.MakeGenericMethod(prop.PropertyType);
var nested = query.Select(p => prop.GetValue(p));
object[] parametersArray = new object[] { pck, nested, i };
genMet.Invoke(null, parametersArray);
}
}
}
class ExcelExtension
{
public void ListToExcel<T>(int pck, IEnumerable<object> nested, int i)
{
}
}
Assuming you are using c# 6.0 or higher. You can use generic type parameters like;
public static void MyMethod<T>(IEnumerable<T> query) where T : IList
{
//Your code here
}
This way, you ensure that T is List of something and reaching indexing won't be a problem.
UPDATE
I misunderstood the question earlier. Here is the updated solution.
public static void MyMethod<T>(IEnumerable<T> query)
{
var t = typeof(T);
var Headings = t.GetProperties();
for (int i = iteratorStart; i < Headings.Count(); i++)
{
if (false == IsValue(Headings[i].PropertyType.FullName))
{
Type type = Type.GetType(Headings[i].PropertyType.FullName);
var mi = typeof(ExcelExtension);
var met = mi.GetMethod("ListToExcel");
var genMet = met.MakeGenericMethod(type);
//Assuming you want to get property value here. IF not You can use like Headings[i].GetName
var nested = query.Select(p =>Convert.ChangeType( Headings[i].GetValue(p),Headings[i].GetType()));
object[] parametersArray = new object[] { pck, nested, i };
genMet.Invoke(null, parametersArray);
}
}
}
Error Explanation:
The problem is in the Select(p => p[something here]) part. Since p is not the property list or array but a type of object, it doesn't contain any indexer. You should use reflection like above example.

How can I create a generic method to compare two list of any type. The type may be a List of class as well

Following is a class
public class Attribute
{
public string Name { get; set; }
public string Value { get; set; }
}
Following is the code in my main method
{
var test = new List<Attribute>();
test.Add(new Attribute { Name = "Don", Value = "21" });
test.Add(new Attribute { Value = "34", Name = "Karthik" });
var test1 = new List<Attribute>();
test1.Add(new Attribute { Name = "Don", Value = "21" });
test1.Add(new Attribute { Value = "34", Name = "Karthik" });
var obj = new Program();
var areEqual1 = obj.CompareList<List<Attribute>>(test, test1);
}
I have a ComapreList method
public bool CompareList<T>(T firstList, T secondList) where T : class
{
var list1 = firstList as IList<T>;
return true;
}
Now, list1 has null. I know that .net does not allow us to do this. But is there any other way where I can cast this generic list. My purpose is to compare each property value of these two list. I am using reflection to get the property but it works only if I can convert the firstlist/secondlist to something enumerable. if I directly use the name of the class in the IList<> (firstList as IList<Attribute>) it works, but not if I give <T>. Please help.
Just create method parameterized by type of lists items type. Even more, you can create method which compares any type of collections:
public bool CompareSequences<T> (IEnumerable<T> first, IEnumerable<T> second,
Comparer<T> comparer = null)
{
comparer = comparer ?? Comparer<T>.Default;
if (first == null)
throw new ArgumentNullException(nameof(first));
if (second == null)
throw new ArgumentNullException(nameof(second));
var firstIterator = first.GetEnumerator();
var secondIterator = second.GetEnumerator();
while(true)
{
bool firstHasItem = firstIterator.MoveNext();
bool secondHasItem = secondIterator.MoveNext();
if (firstHasItem != secondHasItem)
return false;
if (!firstHasItem && !secondHasItem)
return true;
if (comparer.Compare(firstIterator.Current, secondIterator.Current) != 0)
return false;
}
}
If collection items are primitive types, you can use default comparer. But if collections contain custom items, you need either IComparable to be implemented by collection items type:
public class Attribute : IComparable<Attribute>
{
public string Name { get; set; }
public string Value { get; set; }
public int CompareTo (Attribute other)
{
int result = Name.CompareTo(other.Name);
if (result == 0)
return Value.CompareTo(other.Value);
return result;
}
}
Or you can create and pass comparer instance. You can create comparer which is using reflection to compare fields/properties of some type. But it's not as simple as you might think - properties can be complex type or collections.
Usage:
var areEqual1 = obj.CompareSequences(test, test1);
If you don't need to compare objects with complex structure (which have inner collections and other custom objects) then you can use comparer like this one:
public class SimplePropertiesComparer<T> : Comparer<T>
{
public override int Compare (T x, T y)
{
Type type = typeof(T);
var flags = BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance;
foreach (var property in type.GetProperties(flags))
{
var propertyType = property.PropertyType;
if (!typeof(IComparable).IsAssignableFrom(propertyType))
throw new NotSupportedException($"{propertyType} props are not supported.");
var propertyValueX = (IComparable)property.GetValue(x);
var propertyValueY = (IComparable)property.GetValue(y);
if (propertyValueX == null && propertyValueY == null)
continue;
if (propertyValueX == null)
return -1;
int result = propertyValueX.CompareTo(property.GetValue(y));
if (result == 0)
continue;
return result;
}
return 0;
}
}
And pass it to sequence comparer
var equal = obj.CompareSequences(test, test1, new SimplePropertiesComparer<Attribute>());
Change the signature of your method and remove the then redundant cast:
public bool CompareList<T>(IList<T> firstList, IList<T> secondList) where T : class
{
var list1 = firstList as IList<T>; // Cast is not necessary any more
return true;
}
public bool CompareGenericLists<T, U>(List<T> list1, List<U> list2)
{
try
{
if (typeof(T).Equals(typeof(U)))
{
//For checking null lists
if (list1 == null && list2 == null)
return true;
if (list1 == null || list2 == null)
throw new Exception("One of the Lists is Null");
if (list1.Count.Equals(list2.Count))
{
Type type = typeof(T);
//For primitive lists
if (type.IsPrimitive)
{
int flag = 0;
for (int i = 0; i < list1.Count; i++)
{
if (list1.ElementAt(i).Equals(list2.ElementAt(i)))
flag++;
}
if (flag != list1.Count)
throw new Exception("Objects values are not same");
}
//For Reference List
else
{
for (int i = 0; i < list1.Count; i++)
{
foreach (System.Reflection.PropertyInfo property in type.GetProperties())
{
string Object1Value = string.Empty;
string Object2Value = string.Empty;
Object1Value = type.GetProperty(property.Name).GetValue(list1.ElementAt(i)).ToString();
Object2Value = type.GetProperty(property.Name).GetValue(list2.ElementAt(i)).ToString();
if (Object1Value != Object2Value)
{
throw new Exception("Objects values are not same");
}
}
}
}
}
else
throw new Exception("Length of lists is not Same");
}
else
throw new Exception("Different type of lists");
}
catch(Exception ex)
{
throw ex;
}
return true;
}
this method can be used for both primitive and reference lists.try this method.It will compare type,counts,and members of lists.

Is is possible to apply a generic method to a list of items?

Lets say I've written my own method to reverse a list in place.
public static void MyReverse<T>(List<T> source)
{
var length = source.Count;
var hLength = length / 2;
for (var i = 0; i < hLength; i++)
{
T temp = source[i];
source[i] = source[length - 1 - i];
source[length - 1 - i] = temp;
}
}
I call it like so, and it works.
var fooList = new List<Foo>();
MyReverse(fooList);
If I want to reverse multiple lists, I call it like so.
var fooList = new List<Foo>();
var barList = new List<Bar>();
var bazList = new List<Baz>();
MyReverse(fooList);
MyReverse(barList);
MyReverse(bazList);
If I want to reverse an arbitrary number of lists, I'd try:
public static void Main(string[] args)
{
var lists = new List<object>
{
new List<Foo>(),
new List<Bar>(),
new List<Bar>()
};
ReverseLists(lists);
}
public static void ReverseLists(List<object> sourceLists)
{
foreach (var sourceList in sourceLists)
{
MyReverse(sourceList); // Error: Type arguments cannot be inferred from usage
}
}
But this throws a compile time error. Is what I'm trying to do possible - could the ReverseLists method be implemented?
Assuming you have a static method like this
public static class ReverseHelper
{
public static void MyReverse<T>(IList<T> source)
{
var length = source.Count;
var hLength = length / 2;
for (var i = 0; i < hLength; i++)
{
T temp = source[i];
source[i] = source[length - 1 - i];
source[length - 1 - i] = temp;
}
}
}
With the help of non generic interface and a generic class you can do it.
public interface IReverser
{
void Reverse();
}
public class ListReverser<T> : IReverser
{
private readonly IList<T> source;
public ListReverser(IList<T> source)
{
this.source = source;
}
public void Reverse()
{
ReverseHelper.MyReverse<T>(source);
}
}
static void Main(string[] args)
{
var lists = new List<IReverser>
{
new ListReverser<Foo>(new List<Foo>()),
new ListReverser<Bar>(new List<Bar>()),
new ListReverser<Bar>(new List<Bar>())
};
foreach (var reverser in lists)
{
reverser.Reverse();
}
}
I've used IList<T> as opposed to List<T> to support broader number of types; If you want List<T> you can put it back.
As per my comment above...
The compiler cannot covert infer the type of T when passed object (which is effectively what's happening)
However there is a much simpler option - which is to just abandon using generics, and change the signature of your MyReverse method to public static void MyReverse(IList source) (and elsewhere replace List<object> with IList)
ie:
public static void Main(string args[])
{
var lists = new List<IList>
{
new List<Foo>(),
new List<Bar>(),
new List<Bar>()
};
ReverseLists(lists);
}
public static void MyReverse(IList source)
{
var length = source.Count;
var hLength = length / 2;
for (var i = 0; i < hLength; i++)
{
var temp = source[i];
source[i] = source[length - 1 - i];
source[length - 1 - i] = temp;
}
}
public static void ReverseLists(List<IList> sourceLists)
{
foreach (var sourceList in sourceLists)
{
MyReverse(sourceList); // Error: Type arguments cannot be inferred from usage
}
}
public class Foo
{
}
public class Bar
{
}
To compliment the answer from Sriram Sakthivel - the key to your problem is that the Type cannot be inferred from what you are passing in. It's worth noting that List<T> implements IList so your issue above can be reframed using a parameter array of List like:
void Main()
{
var fooList = new List<string>();
var barList = new List<string>();
var bazList = new List<string>();
ReverseLists(fooList, barList, bazList);
}
public static void ReverseLists(params IList [] sourceLists)
{
foreach (var sourceList in sourceLists)
{
MyReverse(sourceList);
}
}
public static void MyReverse(IList source)
{
var length = source.Count;
var hLength = length / 2;
for (var i = 0; i < hLength; i++)
{
var temp = source[i];
source[i] = source[length - 1 - i];
source[length - 1 - i] = temp;
}
}
If you change your ReverseLists method signature to
public static void ReverseLists<T>(IEnumerable<object> sourceLists)
{
foreach (var sourceList in sourceLists.OfType<List<T>>())
{
MyReverse(sourceList);
}
}
Then you can call this for each list type:
ReverseLists<Foo>(lists);
ReverseLists<Bar>(lists);
Maybe not ideal having to call it once per list type, but a comparatively small change to your existing code.
change this line in the original post:
MyReverse(sourceList); // Error: Type arguments cannot be inferred from usage
to this:
MyReverse(((System.Collections.IList)sourceList).Cast<object>().ToList());

PropertyInfo.GetValue() - how do you index into a generic parameter using reflection in C#?

This (shortened) code..
for (int i = 0; i < count; i++)
{
object obj = propertyInfo.GetValue(Tcurrent, new object[] { i });
}
.. is throwing a 'TargetParameterCountException : Parameter count mismatch' exception.
The underlying type of 'propertyInfo' is a Collection of some T. 'count' is the number of items in the collection. I need to iterate through the collection and perform an operation on obj.
Advice appreciated.
Reflection only works on one level at a time.
You're trying to index into the property, that's wrong.
Instead, read the value of the property, and the object you get back, that's the object you need to index into.
Here's an example:
using System;
using System.Collections.Generic;
using System.Reflection;
namespace DemoApp
{
public class TestClass
{
public List<Int32> Values { get; private set; }
public TestClass()
{
Values = new List<Int32>();
Values.Add(10);
}
}
class Program
{
static void Main()
{
TestClass tc = new TestClass();
PropertyInfo pi1 = tc.GetType().GetProperty("Values");
Object collection = pi1.GetValue(tc, null);
// note that there's no checking here that the object really
// is a collection and thus really has the attribute
String indexerName = ((DefaultMemberAttribute)collection.GetType()
.GetCustomAttributes(typeof(DefaultMemberAttribute),
true)[0]).MemberName;
PropertyInfo pi2 = collection.GetType().GetProperty(indexerName);
Object value = pi2.GetValue(collection, new Object[] { 0 });
Console.Out.WriteLine("tc.Values[0]: " + value);
Console.In.ReadLine();
}
}
}
I was most of the way there until I saw this, and I am posting this because I didn't see it anywhere else; the key was using GetValue(collection, new Object[] { i }); in the loop rather than trying to use GetValue(collection, new Object[i]); outside the loop.
(You can probably ignore the "output" in my example);
private static string Recursive(object o)
{
string output="";
Type t = o.GetType();
if (t.GetProperty("Item") != null)
{
System.Reflection.PropertyInfo p = t.GetProperty("Item");
int count = -1;
if (t.GetProperty("Count") != null &&
t.GetProperty("Count").PropertyType == typeof(System.Int32))
{
count = (int)t.GetProperty("Count").GetValue(o, null);
}
if (count > 0)
{
object[] index = new object[count];
for (int i = 0; i < count; i++)
{
object val = p.GetValue(o, new object[] { i });
output += RecursiveWorker(val, p, t);
}
}
}
return output;
}
Assembly zip_assembly = Assembly.LoadFrom(#"C:\Ionic.Zip.Reduced.dll");
Type ZipFileType = zip_assembly.GetType("Ionic.Zip.ZipFile");
Type ZipEntryType = zip_assembly.GetType("Ionic.Zip.ZipEntry");
string local_zip_file = #"C:\zipfile.zip";
object zip_file = ZipFileType.GetMethod("Read", new Type[] { typeof(string) }).Invoke(null, new object[] { local_zip_file });
// Entries is ICollection<ZipEntry>
IEnumerable entries = (IEnumerable)ZipFileType.GetProperty("Entries").GetValue(zip_file, null);
foreach (object entry in entries)
{
string file_name = (string)ZipEntryType.GetProperty("FileName").GetValue(entry, null);
Console.WriteLine(file_name);
}

C# Reflection Indexed Properties

I am writing a Clone method using reflection. How do I detect that a property is an indexed property using reflection? For example:
public string[] Items
{
get;
set;
}
My method so far:
public static T Clone<T>(T from, List<string> propertiesToIgnore) where T : new()
{
T to = new T();
Type myType = from.GetType();
PropertyInfo[] myProperties = myType.GetProperties();
for (int i = 0; i < myProperties.Length; i++)
{
if (myProperties[i].CanWrite && !propertiesToIgnore.Contains(myProperties[i].Name))
{
myProperties[i].SetValue(to,myProperties[i].GetValue(from,null),null);
}
}
return to;
}
if (propertyInfo.GetIndexParameters().Length > 0)
{
// Property is an indexer
}
Sorry, but
public string[] Items { get; set; }
is not an indexed property, it's merely of an array type!
However the following is:
public string this[int index]
{
get { ... }
set { ... }
}
What you want is the GetIndexParameters() method. If the array that it returns has more than 0 items, that means it's an indexed property.
See the MSDN documentation for more details.
If you call property.GetValue(obj,null), and the property IS indexed, then you will get a parameter count mismatch exception. Better to check whether the property is indexed using GetIndexParameters() and then decide what to do.
Here is some code that worked for me:
foreach (PropertyInfo property in obj.GetType().GetProperties())
{
object value = property.GetValue(obj, null);
if (value is object[])
{
....
}
}
P.S. .GetIndexParameters().Length > 0) works for the case described in this article: http://msdn.microsoft.com/en-us/library/b05d59ty.aspx
So if you care about the property named Chars for a value of type string, use that, but it does not work for most of the arrays I was interested in, including, I am pretty sure, a string array from the original question.
You can convert the indexer to IEnumerable
public static IEnumerable<T> AsEnumerable<T>(this object o) where T : class {
var list = new List<T>();
System.Reflection.PropertyInfo indexerProperty = null;
foreach (System.Reflection.PropertyInfo pi in o.GetType().GetProperties()) {
if (pi.GetIndexParameters().Length > 0) {
indexerProperty = pi;
break;
}
}
if (indexerProperty.IsNotNull()) {
var len = o.GetPropertyValue<int>("Length");
for (int i = 0; i < len; i++) {
var item = indexerProperty.GetValue(o, new object[]{i});
if (item.IsNotNull()) {
var itemObject = item as T;
if (itemObject.IsNotNull()) {
list.Add(itemObject);
}
}
}
}
return list;
}
public static bool IsNotNull(this object o) {
return o != null;
}
public static T GetPropertyValue<T>(this object source, string property) {
if (source == null)
throw new ArgumentNullException("source");
var sourceType = source.GetType();
var sourceProperties = sourceType.GetProperties();
var properties = sourceProperties
.Where(s => s.Name.Equals(property));
if (properties.Count() == 0) {
sourceProperties = sourceType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic);
properties = sourceProperties.Where(s => s.Name.Equals(property));
}
if (properties.Count() > 0) {
var propertyValue = properties
.Select(s => s.GetValue(source, null))
.FirstOrDefault();
return propertyValue != null ? (T)propertyValue : default(T);
}
return default(T);
}

Categories