I have a function that generates objects with different data in it (the function fills the object with random data, according to the type). The function returns an object[] as the type is only know at runtime (and it's passed to the function as a parameter).
double[] values;
values = factory.GetData(typeof(double), 10);
Unfortunately I get a compiler error:
Cannot convert from object[] to double[].
How can I cast the object[] programmatically?
EDIT:
this is the original function:
public object[] GetData(Type type, int howMany)
{
var data = new List<object>();
for (var i = 0; i < howMany; i++)
{
data.Add(Convert.ChangeType(GetRandom(type), type));
}
return data.ToArray();
}
where GetRandom() create an object of type type and assign it a random value (random int, random string, random double, only basic types)
and this is the GetRandom() function:
public T GetRandom<T>()
{
var type = typeof(T);
if (type == typeof(int))
{
return prng.Next(0, int.MaxValue);
}
if (type == typeof(double))
{
return prng.NextDouble();
}
if (type == typeof(string))
{
return GetString(MinStringLength, MaxStringLength);
}
if (type == typeof(DateTime))
{
var tmp = StartTime;
StartTime += new TimeSpan(Interval * TimeSpan.TicksPerMillisecond);
return tmp;
}
}
Use Array.ConvertAll:
values = Array.ConvertAll(factory.GetData(typeof(double), 10), item => (double)item);
Example:
object[] input = new object[]{1.0, 2.0, 3.0};
double[] output = Array.ConvertAll(input, element => (double)element); // [1.0, 2.0, 3.0]
Note you might get InvalidCastException if one of the items can't be casted to double.
You could:
values = factory.GetData(typeof(double), 10).Cast<double>().ToArray();
If factory.GetData return an array of object of double, you can use:
values = factory.GetData(typeof(double), 10).Cast<double>().ToArray();
Now all the answers (mostly) actually answer the question there are none that actually talk about using Generics instead. Now this may not fit your direct bill but can be added quite easily to resolve the issue and require no knowledge from a calling application how to understand the return values.
This is simple. Just define an overload that accepts a Generic Type (note T)
public T[] GetData<T>(int count)
{
Type tType = typeof(T);
//.. TODO: Generate our array.. anyway you wish..
List<T> list = new List<T>();
for (int i = 0; i < count; i++)
list.Add(Activator.CreateInstance<T>());
return list.ToArray();
}
So this is a basic example and callable by:
Factory factory = new Factory();
var arr = factory.GetData<double>(10); //returns a typed array of double
Now from a caller perspective we know that the data we are receivining is typed to double or the type they provide.
This is an alternative to your initial question. However if your array of objects will not always be the type originally requested then this will not work.
EDIT
To define the array is really up to how you define your objects but lets just take your initial concept and adapt it to the same above:
public T[] GetData<T>(int count)
{
Type tType = typeof(T);
//.. TODO: Generate our array.. anyway you wish..
List<T> list = new List<T>();
for (int i = 0; i < count; i++)
list.Add((T)GetRandom(tType));
return list.ToArray();
}
In the new sample we are assuming that the Method GetRandom() will return the Type requested. The type requested is generic based on the typereference (typeparam) T. We can get the actual type by calling typeof(T). Now in this example we simply directly cast the GetRandom() object response (I am assuming GetRandom() returns a type of object.
Final Edit
As stated in the comments your can change your object GetRandom(Type type) to T GetRandom<T>(). This will allow you to generate specific types for your random. I would suggest reading up on Generics https://msdn.microsoft.com/en-us/library/512aeb7t.aspx
Now one thing that is not quickly apparent is that what you name your generic is up to you. You dont have to use T and you can use multiple generics in one method call, as with many methods you probably already use.
** Final Final Edit **
Just to elaborate how you could change your GetRandom method to a generic we still have to work with the type object its really the only one that allows for direct boxing conversion for any type. You could use the as keyword but that will could leave to other problems. Now the GetRandom(Type type) method is returning a random object of the type. As stated this is limited to a few types so lets just put together an example.
The first thing to understand is how to handle our various types. Now personally I like to define an interface. So lets define an interface for all our Random Types to inherit. As below:
interface IRandomTypeBuilder
{
object GetNext();
}
As simple interface to return a random typed entity of with the method of GetNext(). This will return a typed response based on the generic parameter T.
Now some simple implementations of this interface.
class DoubleRandomBuilder : IRandomTypeBuilder
{
static Random rng = new Random();
public object GetNext()
{
return rng.NextDouble() * rng.Next(0, 1000);
}
}
class IntRandomBuilder : IRandomTypeBuilder
{
static Random rng = new Random();
public object GetNext()
{
return rng.Next(int.MinValue, int.MaxValue);
}
}
class StringRandomBuilder : IRandomTypeBuilder
{
static Random rng = new Random();
static string aplha = "abcdefghijklmnopqrstuvwxyz";
public object GetNext()
{
string next = "";
for (int i = rng.Next(4, 10), j = i + rng.Next(1, 10); i < j; i++)
next += aplha[rng.Next(0, aplha.Length)];
return next;
}
}
class BoolRandomBuilder : IRandomTypeBuilder
{
static Random rng = new Random();
public object GetNext()
{
return rng.Next(0, 2) % 2 == 0;
}
}
Yes these are very simple but we have 4 different types that all define the GetNext() method and return a random value for the type. Now we can define the GetRandom<T>() method.
public T GetRandom<T>()
{
Type tType = typeof(T);
IRandomTypeBuilder typeGenerator = null;
if (tType == typeof(double))
typeGenerator = new DoubleRandomBuilder();
else if (tType == typeof(int))
typeGenerator = new IntRandomBuilder();
else if (tType == typeof(string))
typeGenerator = new StringRandomBuilder();
else if (tType == typeof(bool))
typeGenerator = new BoolRandomBuilder();
return (T)(typeGenerator == null ? default(T) : typeGenerator.GetNext());
}
Assuming GetData does return doubles boxed as arrays, you can use Cast() to cast all elements to double:
values=factory.GetData(typeof(double), 10).Cast<double>().ToArray();
or you can use OfType() to filter out values that are not double
values=factory.GetData(typeof(double), 10).OfType<double>().ToArray();
A better option though would be to rewrite GetData as a generic method and return a T[]
Related
I'm trying to write a generic function:
private T Convert<T>(List<object> list) where T : new()
{
if (typeof(T).IsArray)
{
T array = new T();
// ... convert here
return array;
}
}
In this scenario, I know that the objects in list should be the same type as the array of T. For example, if T is System.Int32[] then I'd expect the objects in list to all be Int32.
However, T is not always an array. In other cases, it may be a value type or even some other reference type.
How can I convert this List<object> to T given these assumptions?
UPDATE
This seems to be working within a set of given assumptions:
private T Convert<T>(List<object> list)
{
// ... code omitted for brevity
if (typeof(T).IsArray)
{
// big assumption here
Type elementType = Type.GetType(typeof(T).FullName.Replace("[]", string.Empty));
Array array = Array.CreateInstance(elementType, list.Count);
for (int i = 0; i < list.Count; i++)
{
array.SetValue(list[i], i);
}
return (T)Convert.ChangeType(array, typeof(T));
}
}
You can use the ancient ArrayList to create an array of the desired type. (It's pretty much the only thing it is good for.)
private T Convert<T>(List<object> list)
{
if (typeof(T).IsArray)
{
var tmp = new ArrayList(list);
return (T)(object)tmp.ToArray(typeof(T).GetElementType());
}
return (T)list.First();
}
Fiddle
Note: You should probably add some error handling, e.g. if the caller specified a T that doesn't make sense for the list.
I have a use case where I need to check if a value is a C# 7 ValueTuple, and if so, loop through each of the items. I tried checking with obj is ValueTuple and obj is (object, object) but both of those return false. I found that I could use obj.GetType().Name and check if it starts with "ValueTuple" but that seems lame to me. Any alternatives would be welcomed.
I also have the issue of getting each item. I attempted to get Item1 with the solution found here: How do I check if a property exists on a dynamic anonymous type in c#? but ((dynamic)obj).GetType().GetProperty("Item1") returns null. My hope was that I could then do a while to get each item. But this does not work. How can I get each item?
Update - more code
if (item is ValueTuple) //this does not work, but I can do a GetType and check the name
{
object tupleValue;
int nth = 1;
while ((tupleValue = ((dynamic)item).GetType().GetProperty($"Item{nth}")) != null && //this does not work
nth <= 8)
{
nth++;
//Do stuff
}
}
Structures do not inherit in C#, so ValueTuple<T1>, ValueTuple<T1,T2>, ValueTuple<T1,T2,T3> and so on are distinct types that do not inherit from ValueTuple as their base. Hence, obj is ValueTuple check fails.
If you are looking for ValueTuple with arbitrary type arguments, you can check if the class is ValueTuple<,...,> as follows:
private static readonly Set<Type> ValTupleTypes = new HashSet<Type>(
new Type[] { typeof(ValueTuple<>), typeof(ValueTuple<,>),
typeof(ValueTuple<,,>), typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>), typeof(ValueTuple<,,,,,>),
typeof(ValueTuple<,,,,,,>), typeof(ValueTuple<,,,,,,,>)
}
);
static bool IsValueTuple2(object obj) {
var type = obj.GetType();
return type.IsGenericType
&& ValTupleTypes.Contains(type.GetGenericTypeDefinition());
}
To get sub-items based on the type you could use an approach that is not particularly fast, but should do the trick:
static readonly IDictionary<Type,Func<object,object[]>> GetItems = new Dictionary<Type,Func<object,object[]>> {
[typeof(ValueTuple<>)] = o => new object[] {((dynamic)o).Item1}
, [typeof(ValueTuple<,>)] = o => new object[] {((dynamic)o).Item1, ((dynamic)o).Item2}
, [typeof(ValueTuple<,,>)] = o => new object[] {((dynamic)o).Item1, ((dynamic)o).Item2, ((dynamic)o).Item3}
, ...
};
This would let you do this:
object[] items = null;
var type = obj.GetType();
if (type.IsGeneric && GetItems.TryGetValue(type.GetGenericTypeDefinition(), out var itemGetter)) {
items = itemGetter(obj);
}
Regarding the part of the question "How can I get each item?"...
Both ValueTuple and Tuple both implement ITuple, which has a length property and an indexer property. So a the following console app code lists the values to the console:
// SUT (as a local function)
IEnumerable<object> GetValuesFromTuple(System.Runtime.CompilerServices.ITuple tuple)
{
for (var i = 0; i < tuple.Length; i++)
yield return tuple[i];
}
// arrange
var valueTuple = (StringProp: "abc", IntProp: 123, BoolProp: false, GuidProp: Guid.Empty);
// act
var values = GetValuesFromTuple(valueTuple);
// assert (to console)
Console.WriteLine($"Values = '{values.Count()}'");
foreach (var value in values)
{
Console.WriteLine($"Value = '{value}'");
}
Console output:
Values = '4'
Value = 'abc'
Value = '123'
Value = 'False'
Value = '00000000-0000-0000-0000-000000000000'
This is my solution to the problem. A PCL compatible extension class. Special thanks to #dasblinkenlight and #Evk for helping me out!
public static class TupleExtensions
{
private static readonly HashSet<Type> ValueTupleTypes = new HashSet<Type>(new Type[]
{
typeof(ValueTuple<>),
typeof(ValueTuple<,>),
typeof(ValueTuple<,,>),
typeof(ValueTuple<,,,>),
typeof(ValueTuple<,,,,>),
typeof(ValueTuple<,,,,,>),
typeof(ValueTuple<,,,,,,>),
typeof(ValueTuple<,,,,,,,>)
});
public static bool IsValueTuple(this object obj) => IsValueTupleType(obj.GetType());
public static bool IsValueTupleType(this Type type)
{
return type.GetTypeInfo().IsGenericType && ValueTupleTypes.Contains(type.GetGenericTypeDefinition());
}
public static List<object> GetValueTupleItemObjects(this object tuple) => GetValueTupleItemFields(tuple.GetType()).Select(f => f.GetValue(tuple)).ToList();
public static List<Type> GetValueTupleItemTypes(this Type tupleType) => GetValueTupleItemFields(tupleType).Select(f => f.FieldType).ToList();
public static List<FieldInfo> GetValueTupleItemFields(this Type tupleType)
{
var items = new List<FieldInfo>();
FieldInfo field;
int nth = 1;
while ((field = tupleType.GetRuntimeField($"Item{nth}")) != null)
{
nth++;
items.Add(field);
}
return items;
}
}
hackish one liner
type.Name.StartsWith("ValueTuple`")
(can be extended to check the digit at the end)
Consider this chunk of code where I'm testing an unknown variable that could be an int, MyObj, or Tuple, and I'm doing some type checking to see what it is so that I can go on and handle the data differently depending on what it is:
class MyObj { }
// ...
void MyMethod(object data) {
if (data is int) Console.Write("Datatype = int");
else if (data is MyObj) Console.Write("Datatype = MyObj");
else if (data is Tuple<object,object>) {
var myTuple = data as Tuple<object,object>;
if (myTuple.Item1 is int && myTuple.Item2 is int) Console.WriteLine("Datatype = Tuple<int,int>");
else if (myTuple.Item1 is int && myTuple.Item2 is MyObj) Console.WriteLine("Datatype = Tuple<int,MyObj>");
// other type checks
}
}
// ...
MyMethod(1); // Works : Datatype = int
MyMethod(new MyObj()); // Works : Datatype = MyObj
MyMethod(Tuple.Create(1, 2)); // Fails
MyMethod(Tuple.Create(1, new MyObj()); // Fails
// and also...
var items = new List<object>() {
1,
new MyObj(),
Tuple.Create(1, 2),
Tuple.Create(1, new MyObj())
};
foreach (var o in items) MyMethod(o);
My problem is that a Tuple<int,MyObj> doesn't cast to Tuple<object,object>, yet individually, I can cast int to object and MyObj to object.
How do I do the cast?
If you want to check for any pair type, you'll have to use reflection:
Type t = data.GetType();
if(t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Tuple<,>))
{
var types = t.GetGenericArguments();
Console.WriteLine("Datatype = Tuple<{0}, {1}>", types[0].Name, types[1].Name)
}
You may be able to use overloading and dynamic instead of manually inspecting the type:
MyMethod(MyObject obj) { ... }
MyMethod(int i) { ... }
MyMethod(Tuple<int, int> t) { ... }
MyMethod(Tuple<int, MyObject> t) { ... }
foreach(dynamic d in items)
{
MyMethod(d);
}
this will choose the best overload at runtime so you have access the tuple types directly.
As you can see here, Tuple<T1, T2> is not covariant.
Rather than trying to make one method that can take any type of parameter, why not overload your function to recieve valid types you actually expect.
perhaps,
void MyMethod<T1,T2>(Tuple<T1, T2> data)
{
// In case ToString() is overridden
Console.WriteLine("Datatype = Tuple<{0}, {1}>",
typeof(T1).Name,
typeof(T2).Name);
}
void MyMethod(MyObj data)
{
Console.WriteLine("Datatype = MyObj");
}
void MyMethod(int data)
{
Console.WriteLine("Datatype = System.Int32");
}
This way no type checking is required, the compiler does it for you at compile time. This is not javascript, strong typing can be a benefit.
In your code you are explicitly checking against int and MyObj, as well as against their position (Item1 or Item2) as seen here:
if (myTuple.Item1 is int && myTuple.Item2 is int)
else if (myTuple.Item1 is int && myTuple.Item2 is MyObj)
You can do the same explicit check using the same framework you already wrote:
if (data is int) Console.WriteLine("Datatype = int");
else if (data is MyObj) Console.WriteLine("Datatype = MyObj");
else if (data is Tuple<int, int>) Console.WriteLine("Datatype = Tuple<int,int>");
else if (data is Tuple<int, MyObj>) Console.WriteLine("Datatype = Tuple<int,MyObj>");
The downsides to the above code are the same as what you were attempting to do with Tuple<object, object>: You have to explicitly check all combinations of what could go into the tuple as well as their positions, i.e., my code will not work as written if you pass in Tuple<double, double> or Tuple<MyObj, int>, but neither would your code if it actually worked as you wanted it to. If you don't want explicit/hardcoded values then it appears you have to use reflection as seen in Lee's answer, otherwise the above code does what you were intending with less work.
Perhaps the question title is incorrect. I have the following variables
IEnumerable x = // some IEnumerable
System.Type y = // some type
How can iterate over x in order to generate an array with items of type y?
When I look into the internet I found:
public T[] PerformQuery<T>(IEnumerable q)
{
T[] array = q.Cast<T>().ToArray();
return array;
}
Note I cannot call that method PerformQuery becuase y is of type System.Type in other words calling it as PerformQuery<typeof(y)>(x); or PerformQuery<y>(x); will give me a compiler error.
edit
Here is the reason why I have that problem. I have web service where I post to it two things. The type of table I will like to query (example typeof(Customer)), and the actual string query example "Select * from customers"
protected void Page_Load(object sender, EventArgs e)
{
// code to deserialize posted data
Type table = // implement that here
String query = // the query that was posted
// note DB is of type DbContext
IEnumerable q = Db.Database.SqlQuery(table, query );
// here I will like to cast q to an array of items of type table!
You can use Expression Trees:
public static class MyExtensions
{
public static Array ToArray(this IEnumerable source, Type type)
{
var param = Expression.Parameter(typeof(IEnumerable), "source");
var cast = Expression.Call(typeof(Enumerable), "Cast", new[] { type }, param);
var toArray = Expression.Call(typeof(Enumerable), "ToArray", new[] { type }, cast);
var lambda = Expression.Lambda<Func<IEnumerable, Array>>(toArray, param).Compile();
return lambda(source);
}
}
It generates x => x.Cast<Type>().ToArray() for you, with Type known at runtime.
Usage:
IEnumerable input = Enumerable.Repeat("test", 10);
Type type = typeof(string);
Array result = input.ToArray(type);
var ObjectsOfType_y = x.OfType<object>().Where(x => x.GetType() == y);
Notice that this will return an IEnumerable<object>, though. There's no way around that because the type that y (Type) represents is unknown at compile time.
According to my understanding, IENumerable contains only one type. If I understand what you're trying to do, IENumerable already only contains only objects of type y. If y needs to change, you can write an extension method:
public static T[] ToArray<T>(this IEnumerable<T> source)
{
int length = System.Linq.Enumerable.Count(source);
T[] newArray = new T[length];
int i = 0;
foreach(T item in source)
{
newArray[i] = item;
}
return newArray;
}
I am currently writing some code to save general objects to XML using reflection in c#.
The problem is when reading the XML back in some of the objects are structs and I can't work out how to initialise the struct. For a class I can use
ConstructorInfo constructor = SomeClass.GetConstructor(Type.EmptyTypes);
however, for a struct, there is no constructor which takes no parameters so the above code sets constructor to null. I also tried
SomeStruct.TypeInitializer.Invoke(null)
but this throws a memberaccessexception. Google gives no promising hits. Any help would be appreciated.
If the values are structs, they're likely to be immutable - so you don't want to call a parameterless constructor, but the one which takes the appropriate values as constructor arguments.
If the structs aren't immutable, then run away from them as fast as possible, if you can... but if you absolutely have to do this, then use Activator.CreateInstance(SomeClass). You'll have to be very careful when you use reflection to set properties or fields on the value type though - without that care, you'll end up creating a copy, changing the value on that copy, and then throwing it away. I suspect that if you work with a boxed version throughout, you'll be okay:
using System;
// Mutable structs - just say no...
public struct Foo
{
public string Text { get; set; }
}
public class Test
{
static void Main()
{
Type type = typeof(Foo);
object value = Activator.CreateInstance(type);
var property = type.GetProperty("Text");
property.SetValue(value, "hello", null);
Foo foo = (Foo) value;
Console.WriteLine(foo.Text);
}
}
CreateInstance will not help you with structs with no explicitly defined constructors.
FormatterServices.GetUninitializedObject(Type type);
This does the trick with blank structs.
Just to add - with immutable structs, you are likely to have to do parameter matching to the constructor. Unfortunately this is tricky when there can be multiple constructs, and especially since some types have a separate static "Create" method instead of a public constructor. But assuming you've done the matching, you can still use Activator.CreateInstance:
Type type = typeof(Padding); // just an example
object[] args = new object[] {1,2,3,4};
object obj = Activator.CreateInstance(type, args);
However - the code to pick a constructor (the above has 3...) isn't easy. You could say "pick the most complex" and then attempt to match parameter names to property names (case insensitive)...
A naïve example:
static void Main() {
Dictionary<string, object> propertyBag =
new Dictionary<string, object>();
// these are the values from your xml
propertyBag["Left"] = 1;
propertyBag["Top"] = 2;
propertyBag["Right"] = 3;
propertyBag["Bottom"] = 4;
// the type to create
Type type = typeof(Padding);
object obj = CreateObject(type, propertyBag);
}
static object CreateObject(Type type, IDictionary<string,object> propertyBag)
{
ConstructorInfo[] ctors = type.GetConstructors();
// clone the property bag and make it case insensitive
propertyBag = new Dictionary<string, object>(
propertyBag, StringComparer.OrdinalIgnoreCase);
ConstructorInfo bestCtor = null;
ParameterInfo[] bestParams = null;
for (int i = 0; i < ctors.Length; i++)
{
ParameterInfo[] ctorParams = ctors[i].GetParameters();
if (bestCtor == null || ctorParams.Length > bestParams.Length)
{
bestCtor = ctors[i];
bestParams = ctorParams;
}
}
if (bestCtor == null) throw new InvalidOperationException(
"Cannot create - no constructor");
object[] args = new object[bestParams.Length];
for (int i = 0; i < bestParams.Length; i++)
{
args[i] = propertyBag[bestParams[i].Name];
propertyBag.Remove(bestParams[i].Name);
}
object obj = bestCtor.Invoke(args);
// TODO: if we wanted, we could apply any unused keys in propertyBag
// at this point via properties
return obj;
}