C# Cast Entire Array? - c#

I see this Array.ConvertAll method, but it requires a Converter as an argument. I don't see why I need a converter, when I've already defined an implicit one in my class:
public static implicit operator Vec2(PointF p)
{
return new Vec2(p.X, p.Y);
}
I'm trying to cast an array of PointFs to an array of Vec2s. Is there a nice way to do this? Or should I just suck it up and write (another) converter or loop over the elements?

The proposed LINQ solution using Cast/'Select' is fine, but since you know you are working with an array here, using ConvertAll is rather more efficienct, and just as simple.
var newArray = Array.ConvertAll(array, item => (NewType)item);
Using ConvertAll means
a) the array is only iterated over once,
b) the operation is more optimised for arrays (does not use IEnumerator<T>).
Don't let the Converter<TInput, TOutput> type confuse you - it is just a simple delegate, and thus you can pass a lambda expression for it, as shown above.

As an update to this old question, you can now do:
myArray.Cast<Vec2>().ToArray();
where myArray contains the source objects, and Vec2 is the type you want to cast to.

Cast doesn't consider user defined implicit conversions so you can't cast the array like that. You can use select instead:
myArray.Select(p => (Vec2)p).ToArray();
Or write a converter:
Array.ConvertAll(points, (p => (Vec2)p));
The latter is probably more efficient as the size of the result is known in advance.

The most efficient way is:
class A { };
class B : A { };
A[] a = new A[9];
B[] b = a as B[];

This is an answer related to a bounty question asked by #user366312 and not to the original question.
What would be the solution in the case of .NET Framework 2.0?
As far as I know, LINQ was introduced to the.NET framework with version 3.5 back in 2007. So this means it is not possible to use LINQ in .NET Framework 2.0.
Therefore I would use just a regular for-loop and cast each element in the array.
Something like this without testing it:
var newArray = new NewType[myArray.Length];
for (var i = 0; i < myArray.Length; i++)
{
newArray[i] = (NewType)myArray[i];
}
You can wrap it up in a method like:
public static NewType[] ConvertAll(Vec2[] myArray)
{
var newArray = new NewType[myArray.Length];
for (var i = 0; i < myArray.Length; i++)
{
newArray[i] = (NewType)myArray[i];
}
return newArray;
}
And use it
var newArray = ConvertAll(myArray);

Related

How do I convert array (decimal[]) to dynamic[] array?

How do I convert array of known type, for instance, decimal to array of dynamic - decimal[] to dynamic[]`?
I can do this manually, but I wonder whether there is something more sophisticated?
decimal[] decArray = someMethodReturnsDecimalArr();
// now conversion is needed
dynamic[] res = new dynamic[decArray.Count()];
for (var i = 0; i < res.Count(); i++)
{
res[i] = boundaries[i];
}
return res;
Why do I need this?
First, I really need this! I know that if the code would be written from scratch and by me, maybe I would use generics or something like that. But I cannot change that part of code that returns dynamic[].
So, there is class with dynamic[] properties:
public class Info
{
public dynamic[] Points { get; set; }
...
}
Based on different complicated criterias sometimes Points are decimal, sometimes DateTimes or there are also multiple other possibilities. This is based on data coming from database and also from UI.
This is not my design and I cannot changed it.
There is methodX that returns Info class.
My part is implement methods for some specific calculations and these methods return decimal[] or other types. Later I have to convert to dynamic[] so it would work with already existing methods.
decimal[] decArray = someMethodReturnsDecimalArr();
or
double[] doubleArray = someMethodReturnsDoubleArr();
And all these arrays have to be set to Points.
(I tried to simplify description here).
Use LINQ:
dynamic[] res = decArray.Cast<dynamic>().ToArray();
Use linq, method Cast
var res = decArray.Cast<dynamic>();

Cannot implicitily convert type `double[]` to `System.Collections.Generic.List<double>`

I am getting the following error message with the code below. I thought the data type List<double> was the same as double[] but that C# required it to be instantiated using the first syntax for the variable to work as an object. What am I doing wrong or is my thinking wrong?
Cannot implicitily convert type `double[]` to `System.Collections.Generic.List<double>`
Code:
private void RunScript(List<Curve> crv, double nb, ref object DivPts)
{
List<double> nbtemp = new List<double>();
List<double> Divpt = new List<double>();
for(int i = 0; i < crv.Count;i = i + 2)
{
nbtemp = crv[i].DivideByLength(nb, true);
}
Divpt = nbtemp;
No, a list is not an array, even though the concepts are somewhat similar. The List<T> class in C# is actually implemented with a behind-the-scenes array.
If you want to set a List from an array, you can use something like this:
nbtemp = new List<double>(crv[i].DivideByLength(nb, true));
that will create a new List, and initialize it with the array. You can also use the AddRange method of the List, if you would like to append an array to an existing list, like this:
nbtemp.AddRange(crv[i].DivideByLength(nb, true));
You can't convert from Array to List, but you can easily call:
nbtemp = crv[i].DivideByLength(nb, true).ToList();
Or, since you already have to Lists defined, you could also:
nbtemp.AddRange(crv[i].DivideByLength(nb, true));
You are using an assignment and it is hard to tell what DivideByLength returns, if a single value then use:
nbtemp.Add(crv[i].DivideByLength(nb, true));
Otherwise, if it is returning an array, try changing your definition to allow the list to contain arrays:
List<double[]> nbtemp = new List<double[]>();
Note that List is not equivalent to double[]. List has many features that a simple array does not. You can see the differences by looking at the two different MSDN articles for which methods are publicly exposed.
List
Array
Also, your for loop as it stands is using an assignment. Without a change to that part of the code, you will only assign the last iteration of the for loop to the variable nbtemp (assuming you remove the error)
They are both IEnumerable implementers but they are not equivalent types. You will need to perform a cast or a method call. In the code above I would say you'd need:
nbtemp = (crv[i].DivideByLength(nb, true)).ToList();
or
nbtemp.AddRange(crv[i].DivideByLength(nb, true));

Generic way to send an array collection containing only a part of a more complex structure

Let's say a program like this:
class MyClass
{
public int Numbers;
public char Letters;
}
class Program
{
static void Main()
{
var mc = new MyClass[5];
for (var i = 0; i < 5; i++)
{
mc[i].Numbers = i + 1;
mc[i].Letters = (char) (i + 65);
}
}
}
Now, let's suppose an 'X' method that requires ALL the numbers contained in the object mc, in a separate array, that's sent as a parameter.
My first idea is a for, a new integers array, and copy one by one onto its respective position. But, what if the MyClass gets different, now it has strings and floats, and I wanna pull out the strings, now the for has to be completely redefined in its inside part to create the needed array for another 'X' method.
I know of cases where Linq helps a lot, for example, generics for Sum, Average, Count and another numeric functions, and of course, it's combination with lambda expressions.
I'm wondering if something similar exists to make the above arrays of MyClass (and anothers of course) in a faster-generic way?
If you want to use LINQ, you can do something like the following:
int [] numbers = mc.Select<MyClass, int>(m => mc.Number).ToArray();
To make it more generic than that, it gets a bit more complicated, and you may need reflection, or dynamic objects. A simple example with reflection would be:
private TValue[] ExtractFields<TClass, TValue>(TClass[] classObjs, string fieldName)
{
FieldInfo fInfo = typeof(TClass).GetField(fieldName, BindingFlags.Public | BindingFlags.Instance);
if (fInfo != null && fInfo.FieldType.Equals(typeof(TValue)))
return classObjs.Select<TClass, TValue>(c => (TValue)fInfo.GetValue(c)).ToArray();
else
throw new NotSupportedException("Unidentified field, or different field type");
}
And then just call it like:
int [] fields = ExtractField<MyClass, int>(mc, "Number");
If you are using C# 4.0, then you may use dynamic
class MyClass
{
public dynamic Numbers;
public char Letters;
}
EDIT: based on comments
I am not sure if this is what you want:
int[] arr = mc.Select(a => a.Numbers).ToArray<int>();
or without casting
int[] arr = mc.Select(a => a.Numbers).ToArray();
Why not just use Dictionary<int, char>, or if the data type is unknown then simply Dictionary<object, object>
If your goal is to generate a new array which is detached from the original array, but contains data copied from it, the most generic thing you could do would be to define a method like:
T my_array[]; // The array which holds the real things
U[] CopyAsConvertedArray<U>(Func<T,U> ConversionMethod);
That would allow one to generate a new array which extracts items from the original using any desired method.

C#: Dynamically Constructing Variables

I get from an input a group of double variables named: weight0, weight1...weight49.
I want to dynamically insert them into a double Array for easier manipulation.
But instead of calling each one like: Weights[0] = weight0...Weights[49] = weight49 I want to do it with a single loop.
Is there a way to do it?
No, basically - unless you mean at the same time that you create the array:
var weights = new[] {weight0, weight1, weight2, ... , weight48, weight49};
Personally, I'd be tempted to get rid of the 50 variables, and use the array from the outset, but that may not be possible in all cases.
you could use reflection to determine the index of the array from the variable names but this is far from efficient. See this post for details.
I would try to do it with a KeyValuePair- Listobject
// sample data
var weight = 1.00;
// create a list
var tmp = new List<KeyValuePair<string,object>>();
// Here you can add your variables
tmp.Add(new KeyValuePair<string,object>("weights" + tmp.Count.ToString()
, weight));
// If needed convert to array
var weights = tmp.ToArray();
// get the information out of the array
var weightValue = weights[0].Value;
var weightKey = weights[0].Key;
I think this will give you all the options, you might need for the array. Give it a try.
I'm putting this up because you can do it - so long as these variables are actually fields/properties. Whether you should is another matter - this solution, while reusable, is slow (needs delegate caching) and I have to say I agree with Marc Gravell - consider using an array throughout if you can.
If the variables are properties then it needs changing. Also if you need to write the array back to the variables in one shot (because this solution generates an array with copies of all the doubles, I wouldn't consider creating an object array with boxed doubles), that requires another method...
So here goes. First a holy wall of code/extension method:
//paste this as a direct child of a namespace (not a nested class)
public static class SO8877853Extensions
{
public static TArray[] FieldsToArray<TObj, TArray>(this TObj o,string fieldPrefix)
{
if(string.IsNullOrWhiteSpace(fieldPrefix))
throw new ArgumentException("fieldPrefix must not null/empty/whitespace",
"fieldPrefix");
//I've done this slightly more expanded than it really needs to be...
var fields = typeof(TObj).GetFields(System.Reflection.BindingFlags.Instance
| System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.NonPublic)
.Where(f =>f.Name.StartsWith(fieldPrefix) && f.FieldType.Equals(typeof(TArray)))
.Select(f =>new{ Field = f, OrdinalStr = f.Name.Substring(fieldPrefix.Length)})
.Where(f => { int unused; return int.TryParse(f.OrdinalStr, out unused);})
.Select(f => new { Field = f.Field, Ordinal = int.Parse(f.OrdinalStr) })
.OrderBy(f => f.Ordinal).ToArray();
//doesn't handle ordinal gaps e.g. 0,1,2,7
if(fields.Length == 0)
throw new ArgumentException(
string.Format("No fields found with the prefix {0}",
fieldPrefix),
"fieldPrefix");
//could instead bake the 'o' reference as a constant - but if
//you are caching the delegate, it makes it non-reusable.
ParameterExpression pThis = Expression.Parameter(o.GetType());
//generates a dynamic new double[] { var0, var1 ... } expression
var lambda = Expression.Lambda<Func<TObj, TArray[]>>(
Expression.NewArrayInit(typeof(TArray),
fields.Select(f => Expression.Field(pThis, f.Field))), pThis);
//you could cache this delegate here by typeof(TObj),
//fieldPrefix and typeof(TArray) in a Dictionary/ConcurrentDictionary
return lambda.Compile()(o);
}
}
The extension method above will work on any type. It's generic over both the instance type and desired array type to simplify the creation of the lambda in code - it doesn't have to be generic though.
You pass in the name prefix for a group of fields - in your case "weight" - it then searches all the public and private instance fields for those with that prefix that also have a suffix which can be parsed into an integer. It then orders those fields based on that ordinal. It does not check for gaps in the ordinal list - so a type with weight0 and weight2 would work, but would only create a two-element array.
Then it bakes a dynamic piece of code using Expression trees, compiles it (at this point, as mentioned in the code, it would be good to cache the delegate against TObj and TArray for future use) and then executes it, returning the result.
Now add this to a test class in a standard unit test project:
private class SO8877853
{
private double field0 = 1.0;
private double field1 = -5.0;
private double field2 = 10.0;
public double[] AsArray()
{
//it would be nice not to have to pass both type names here - that
//can be achieved by making the extension method pass out the array
//via an 'out TArray[]' instead.
return this.FieldsToArray<SO8877853, double>("field");
}
}
[TestMethod]
public void TestThatItWorks()
{
var asArray = new SO8877853().AsArray();
Assert.IsTrue(new[] { 1.0, -5.0, 10.0 }.SequenceEqual(asArray));
}
Like I say - I'm not condoning doing this, nor am I expecting any +1s for it - but I'm a sucker for a challenge :)

Multidimensional arrays do not implement IEnumerable<T>, or do they?

For the reasons that I still do not understand (see this SO question) multidimensional arrays in CLR do not implement IEnumerable<T>. So the following does not compile:
var m = new int[2,2] {{1, 2}, {3, 4}};
var q = from e in m select e;
Then how come that this works just fine in VB.NET?
Sub Main()
Dim m(,) As Integer = {{1, 2}, {3, 4}}
Dim q = From e In m Select e
For Each i In q
Console.WriteLine(i)
Next
End Sub
Update:
The following code works because the C# compiler replaces the foreach with for loops to go through each dimension.
foreach(var e in m)
Console.WriteLine(e);
becomes
int[,] numArray3 = new int[,] { { 2, 2 }, { 3, 3 } };
int upperBound = numArray3.GetUpperBound(0);
int num4 = numArray3.GetUpperBound(1);
for (int i = numArray3.GetLowerBound(0); i <= upperBound; i++)
{
for (int j = numArray3.GetLowerBound(1); j <= num4; j++)
{
int num = numArray3[i, j];
Console.WriteLine(num);
}
}
The query works in VB.Net because it gets transformed into
IEnumerable<object> q = m.Cast<object>().Select<object, object>(o => o);
This works because you can call Cast<TResult>() on IEnumerable, which [*,*] implements.
The LINQ query doesn't work in C# because of the different approach the C# and VB.Net designers took. VB.Net takes a more hand holding approach and fixes your mistake and converts IEnumerable to IEnumerable<object> so it can be used.
In C#, you can simulate this by using
var q = from e in m.Cast<object>() select e;
There are two reasons they don't implement it natively in C#:
There's more than one way you could do it. Do you want each 'cell', or do you want each 'row'? And how do you define 'row': [], IEnumerable, other? What if there are more than two dimensions? As soon as they pick one way, an army of developers will tell them they should have done it a different way.
Thanks to iterator blocks and the yield keyword, it just so easy to implement your own that's specific to your need at the time. Of course, that's a C# construct, but it's not that much harder in VB.
The MSDN page for Array includes this:
Important Note:
In the .NET Framework version 2.0, the Array class implements the System.Collections.Generic.IList<T>, System.Collections.Generic.ICollection<T>, and System.Collections.Generic.IEnumerable<T> generic interfaces. The implementations are provided to arrays at run time,
Note the final words in the quote... it appears this generation does not happen for multi-dimensional arrays (so a documentation bug).
But as others have noted, what would T be? A good case can be made for T[] (or, these days with LINQ, IEnumerable<T>).
In the end, if you want to iterate all the array's members just stick with IEnumerable and Cast<T> extension. Otherwise easy to write your own.
Tip: instead of Cast<object>() use a typed range variable
Samuel stated:
In C#, you can simulate this by using
var q = from e in m.Cast<object>() select e;
// q is of type IEnumerable<object>
which is of course correct as far as mimicking VB in C# is concerned, but you would loose your type information. Instead, it is much easier and as it turns out, slightly better readable, to simply declare your range variable.
The following compiles, performs better, is type safe and does not loose type information:
var m = new int[2, 2] { { 1, 2 }, { 3, 4 } };
var q = from int e in m select e;
// q is of type IEnumerable<int>
In the original suggestion, you would have an IEnumerable<object>, using int e you change that into IEnumerable<int>, which has its advantages.

Categories

Resources