I would like to convert T to T[] if it is an array.
static T GenericFunction<T>(T t)
{
if (t == null) return default(T);
if (t.GetType().IsArray)
{
//if object is an array it should be handled
//by an array method
return (T) GenericArrayFunction((T[])t);
}
...
}
static T[] GenericArrayFunction<T>(T[] t)
{
if (t == null) return default(T);
for (int i = 0 ; i < t.Length ; i++)
{
//for each element in array carry
//out Generic Function
if (t[i].GetType().IsArray())
{
newList[i] = GenericArrayFunction((T[])t[i]);
}
else
{
newList[i] = GenericFunction(t[i]);
}
}
...
}
Error If I try (T[])t
Cannot convert type 'T' to 'T[]'
Error If I just try to pass t
The type arguments for method 'GenericArrayFunction(T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly.
Judging from your particular example, could you not define two methods and let the compiler choose the correct one when an array is passed in?
using System;
class Program
{
static T GenericFunction<T>(T t)
{
Console.WriteLine("GenericFunction<T>(T)");
return default(T);
}
static T[] GenericFunction<T>(T[] t)
{
// Call the non-array function
for(int i = 0; i < t.Length; ++i)
t[i] = GenericFunction(t[i]);
Console.WriteLine("GenericFunction<T>(T[])");
return new T[4];
}
static void Main()
{
int[] arr = {1,2,3};
int i = 42;
GenericFunction(i); // Calls non-array version
GenericFunction(arr); // Calls array version
}
}
Just because T is an array type doesn't mean that it's also an array of T. In fact, the only way that could happen would be for T to be something like object, Array or one of the interfaces implemented by arrays.
What are you really trying to do? I suspect you want to find out the element type of the array, and then call GenericArrayFunction with the appropriate T - but that won't be the same T, and you'll need to call it with reflection, which will be somewhat painful. (Not too bad, but unpleasant.)
I suspect you don't fully understand C#/.NET generics - please give us more context about the bigger picture so we can help you better.
EDIT: The reflection approach would be something like this:
private static readonly ArrayMethod = typeof(NameOfContainingType)
.GetMethod("GenericArrayFunction", BindingFlags.Static | BindingFlags.NonPublic);
...
static T GenericFunction<T>(T t)
{
if (t == null) return default(T);
if (t is Array)
{
Type elementType = t.GetType().GetElementType();
MethodInfo method = ArrayMethod.MakeGenericMethod(new[] elementType);
return (T) method.Invoke(null, new object[] { t });
}
...
}
Note that this will still fail for rectangular arrays, which get even harder to cope with.
It is not possible. T can never be T[]. T is always certain type, not just placeholder. If T is array (int[]) then T[] will be int[][].
Edit: There are some exceptions (like object is object[]), but in general case (and thats what generics are) T can't be T[]
Related
I'm working on the following code for a uni project:
public Order[] collapse()
{
return ConcatArrays(Rondes.Select(rs => rs.collapse()));
}
public static T[] ConcatArrays<T>(T[][] list)
{
var result = new T[list.Sum(a => a.Length)];
int offset = 0;
for (int x = 0; x < list.Length; x++)
{
list[x].CopyTo(result, offset);
offset += list[x].Length;
}
return result;
}
Here rs is of a custom type Ronde, and rs.collapse() returns Order[]. The goal of the first function is to compute rs.collapse() for every rs, and concatenate these arrays. How do I resolve the following error:
The type arguments for method ConcatArrays<T>(T[][]) cannot be inferred from the usage. Try specifying the type arguments explicitly.
I tried to follow the reccomendation by changing all T's to Order, but that did not change the error message. Any help is greatly appreciated!! Thanks in advance!
EDIT:
I have now changed the first function to:
public Order[] collapse()
{
if (Rondes == null) { return new Order[0]; }
return OrderLijsten.ConcatArrays<Order>(Rondes.Select(rs => rs.collapse()));
}
but now i get this error:
Error CS1503 Argument 1: cannot convert from 'System.Collections.Generic.IEnumerable<Grote_Opdracht.Classes.Order[]>' to 'Grote_Opdracht.Classes.Order[][]'
The error message gives you a pretty clear explanation: Your Select call returns an IEnumerable but this ConcatArrays implementation expects an array (of arrays).
You could either make the method use IEnumerable (but then you would enumerate multiple times), or call ToArray to make an array. In this case, I would prefer a combination, and also use ToList instead, which should be the most performant option:
public Order[] Collapse()
{
// make sure to iterate only once
var arrays = Rondes.Select(rs => rs.collapse()).ToList();
return ConcatArrays(arrays);
}
// use IEnumerable to be more flexible
public static T[] ConcatArrays<T>(IEnumerable<T[]> arrays)
{
var result = new T[arrays.Sum(a => a.Length)];
int offset = 0;
foreach (var a in arrays)
{
a.CopyTo(result, offset);
offset += a.Length;
}
return result;
}
Can a 'generic type' be an array?
And in the cases where it is, how can one access that array?
Can you access a given generic type T as an array, when it is one, and as a non-array when it is not one?
For instance:
If I had a method like ->
void MustBeOfType<T>(T value){}
Can I have ->
MustBeOfType<int>(10);
And Also ->
MustBeOfType<int[]>( new int[] { 1, 2, 3 } );
And within those generic methods, can they access the values as one would expect? - one as an int and one as an int[]?
I think there might be something with typeof(T).IsArray()... but I just can't for the life of me figure out how to cast the parameter as an array when it is one.
Thanks!
You could...but, I'm not sure you should:
void MustBeOfType<T>(T value)
{
Array array = value as Array;
if (array != null) //It is an array
{
foreach (var arrayItem in array)
{
}
for (int i = 0; i < array.Length; i++)
{
var arrayItem = array.GetValue(i);
}
}
else //It is not an array
{
}
}
I am not sure what you are trying to do but Generic types can be anything. You can put restrictions to your generic type with where clause, but this is up to you and up to functionality and context.
Lets take List as example. Let say that we have List inside List. Then we define it as:
List<List<string>> myListOfList = new List<List<string>>();
your must be of type can also be anything ( if you didnt put restriction with where clause)
MustBeOfType<int[][]>()
MustBeOfType<List<List<string>>>()
MustBeOfType<AnyOtherGenericClass<List<string>>>()
and to be able to access it:
class MustBeOfType<T>
{
private T _value;
MustBeofType(T value)
{
_value = value;
}
}
to be able to make operation on , you can use reflection or if you put where restriction and your where restriction has Type, then you can see properties of that Type.
I am trying to send a params object[i] to a T function but I cant find the right syntax.
Here is an example to show the context of what I am trying to achieve:
private bool decodeValue<T>(int id,ref T item, string code)
{
if(!TypeDescriptor.GetConverter (item).CanConvertFrom(typeof(string)))
{
errorThrow (id + 2);
return false;
}
try
{
item = ((T)TypeDescriptor.GetConverter (item).ConvertFromString (code));
}
catch
{
errorThrow (id + 2);
//item = default(T);
return false;
}
return true;
}
private bool decodeValues(string[] code, int id, params object[] items)
{
for (int i = 0; i < items.Length; i++)
{
System.Type t = items [i].GetType();
//in this part i cant find the correct syntax
if(decodeValue<(t)items[i]>(id, ref items[i] as t, code[i+2]))
{
}
}
return false;
}
At the line decodeValue<(t)items[i]>(id, ref items[i] as t, code[i+2]
no matter what syntax I try, the compiler tells me that after > it expects a )
I could inline the function decodeValue into the for loop but I think there is a more elegant way of doing it. Any suggestions?
In your example, you replace content of items. Why do even need templated function? Just do this:
private bool decodeValue(int id,ref object item, string code)
{
if(!TypeDescriptor.GetConverter(item).CanConvertFrom(typeof(string)))
{
errorThrow (id + 2);
return false;
}
try
{
item = TypeDescriptor.GetConverter(item).ConvertFromString(code);
}
catch
{
errorThrow (id + 2);
return false;
}
return true;
}
private bool decodeValues(string[] code, int id, params object[] items)
{
for (int i = 0; i < items.Length; i++)
{
//in this part i cant find the correct syntax
if(decodeValue(id, ref items[i], code[i+2]))
{
}
}
return false;
}
If you ever need type of item in your code, just call .GetType() where it actualy needed. That is not only good way to do things, but in some cases performance effective (compiler can greatly optimise some calls of this function).
generics (<T>) and reflection (.GetType()) are not good friends. There is no convenient way to express that. You can, however, abuse dynamic to do it, but you can't easily use ref in that case. Further, you can't ref between an object and a T. So basically, there's a lot of hurdles. Frankly, I think you should assess whether decodeValue<T> really needs to be generic. In this scenario - especially when using TypeDescriptor - I very much doubt that it needs to be. I suspect your best bet would be to have:
private bool DecodeValue(int id, ref object item, Type type, string code)
The generics' syntax if for compile time while GetType() is for runtime. You cannot mix them of use them together.
A generic class is resolved while you source is converted to binary. The type or interface provided in a generic type is used in that area.
On the contrary GetType returns a type during execution. That type cannot be inserted in a generic definition because compilation has already be done at that time.
What I'm trying to do is without generic types is simple.
SomeType[] s = new SomeType[5];
for(int i = 0; i < 5: i++){
s[i]= new SomeType();
}
Using generics this is what I have so far.
private static T TypeMapper<T>(dynamic handle) {
if (typeof(T).IsArray)
{
Type elementType = typeof(T).GetElementType();
int arrayLength = handle.Length;
Array array = Array.CreateInstance(elementType, arrayLength);
//??? How to get the type of an generic array element
Type typeOfthis = typeof(array[0]).GetElementType();
for (int i = 0; i < arrayLength; i++)
{
//??? How to create an instance from it.
array[i] = new typeOfthis ();
}
T obj = (T)(object)array;
return obj;
}
else
{}
Then calling the TypeMapper function with.
dynamic t = new TextSpan[4];
var result = TypeMapper<TextSpan[]>(t)
How can I get the type of an generic array element.
Type typeOfthis = typeof(array[0]).GetElementType();//Not Working
And how to create an instance from it.
array[i] = new typeOfthis ();
Your help is highly appreciated.
The Solution of Rubidium 37 was marked as correct
Type typeofitem = array.GetType().GetElementType();
instead of
Type typeOfthis = typeof(array[0]).GetElementType();
and
array.SetValue(Activator.CreateInstance(typeofitem), i);
instead of
array[i] = new typeOfthis ();
Thanks also for the other solutions here, but they quite missing the point that I can pass SomeType[] or SomeType as T to TypeMapper<T>(dynamic handle).
Then I can then derive from the type if it is an array or not and process the handle as needed.
offtopic
The long breath reason behind it is to marshal javascript types from v8 used by the v8dotnet wrapper which returns objects which have an equal behavior like dynamic objects.Instead of explaining what an InternalHandle handle is I reduced it to dynamic so that it is comprehensible.
When you have an array element, you have an object, that can be a different type from other array elements (like a derived type).
Then you can get the type of a single element array as you do for any standalone object.
You should replace
Type typeOfthis = typeof(array[0]).GetElementType();
with
Type typeOfthis = array[0].GetType();
Whereas, when you want create an object of any type, you can simply using Activator.CreateInstance or retrieve one ConstructorInfo from the Type.
Replace
array[i] = new typeOfthis ();
with
array[i] = Activator.CreateInstance(typeOfthis);
Just for you yo know..
- Remember that Activator.CreateInstance will try to call a parameterless constructor of the type you pass to it, then that type need to have such a constructor.
- The object you create need to be compatible with the array's Element type (assignable to it, like a derived class).
- If you know in advance that all elements of the array are (or should be) of the same type, then you don't need to get the type for each element; instead, typeof(T).GetElementType() should be enough.
- If the element type is a class and you don't need to assign concrete values because they are assigned later, or you know that the elementi type is struct, then you don't need to fill the array.
A part of that details, if you want to create a new array, given its length and the (concrete) type of its elements, you can try one of the following:
public static Array CreateAndFillArray(Type type, int length)
{
var result = Array.CreateInstance(type, length);
for (int ixItem = 0; ixItem < result.Length; ixItem++)
result.SetValue(Activator.CreateInstance(type), ixItem);
return result;
}
public static T[] CreateAndFillArray<T>(int length)
{
var type = typeof(T);
var result = new T[length];
for (int ixItem = 0; ixItem < result.Length; ixItem++)
result[ixItem] = (T)Activator.CreateInstance(type);
return result;
}
public static T[] CreateAndFillArray<T>(int length)
where T : new()
{
var result = new T[length];
for (int ixItem = 0; ixItem < result.Length; ixItem++)
result[ixItem] = new T();
return result;
}
Regards,
Daniele.
You need to learn something more about generics (and generalization in general). If you want to fill an array with default instances of a type, you can use this:
void ConstructAll<T>(T[] array) where T : new()
{
for (var i = 0; i < array.Length; i++)
{
array[i] = new T();
}
}
However, this doesn't really look like a good idea. What problem are you trying to solve? And why would you think using dynamic is a good solution?
I have an array of objects (object[]). All the items in this array have the same type (unknown at compile time). I need to convert this array in a typed array, that is, if the items are integers, I want to get an int[].
I've looked into the Array.ConvertAll method, but I need to specify a specific type in the template, meaning that I have to get the element type then call ConvertAll for each case possible.
I've also looked into the keyword dynamic with no more luck (looks like dynamic[] is the same as object[]).
How can I achieve this?
It sounds like you want something like:
dynamic array = Array.CreateInstance(input[0].GetType(), input.Length);
for (int i = 0; i < input.Length; i++)
{
array[i] = (dynamic) input[i];
}
Here the dynamic just handles the conversion part for you.
Alternatively:
public static Array ConvertArray(object[] input)
{
dynamic sample = input[0]; // Just used for type inference
return ConvertArrayImpl(sample, input);
}
private static T[] ConvertArrayImpl<T>(T sample, object[] input)
{
return input.Cast<T>().ToArray();
}
You could do make the ConvertArrayImpl call with reflection manually of course, instead of using dynamic typing.
Also note that all of these will fail if the input array is empty...
Similar to Jon's solution you can do without dynamic and make use of the Array type:
public Array Convert(Array a) {
if (a.GetLength(0) == 0){
return new int[0];
}
var type = a.GetValue(0).GetType();
var result = Array.CreateInstance(type, a.GetLength(0));
for (int i = 0; i < a.GetLength(0); i++) {
result.SetValue(a.GetValue(i), i);
}
return result;
}