I'm trying to convert an Enum array to an int array:
public enum TestEnum
{
Item1,
Item2
}
int[] result = Array.ConvertAll<TestEnum, int>(enumArray, new Converter<TestEnum, int>(Convert.ToInt32));
For some reason Convert.ToInt32 doesn't work when used in Array.ConvertAll, so I had to make some changes:
int[] result = Array.ConvertAll<TestEnum, int>(enumArray, new Converter<TestEnum, int>(ConvertTestEnumToInt));
public static int ConvertTestEnumToInt(TestEnum te)
{
return (int)te;
}
Just out of curiosity, is there any way to have this working without using an extra method?
Regards
Just cast using an anonymous method:
int[] result = Array.ConvertAll<TestEnum, int>(
enumArray, delegate(TestEnum value) {return (int) value;});
or with C# 3.0, a lambda:
int[] result = Array.ConvertAll(enumArray, value => (int) value);
Luckily for us, C# 3.0 includes a Cast operation:
int[] result = enumArray.Cast<int>().ToArray();
If you stop using arrays and start using IEnumerable<>, you can even get rid of the ToArray() call.
enumArray.Select(x => (int) x)).ToArray()
This worked like a charm:
var intArray = enumArray.Select(e => (int)e).ToArray();
as did this:
var intList = enumArray.Select(e => (int)e).ToList();
FYI: tested on .Net4ClientProfile and VS2010
Actually, you don't even need to use LINQ. You can just cast it in a normal way, provided that you drop the type down to object.
Having:
enum One { one0, one1, one2, one3 };
enum Two { two0, two1, two2, two3 };
One[] vals = new One[] { One.one0, One.one3 };
we can play:
//Two[] aa = (Two[])vals; // impossible
Two[] aa = (Two[])(object)vals; // possible!
Two bb = aa[1]; // == Two.two3
At first, I was really suprised that the second line doesn't throw InvalidCast. But it does not.
Looking at the types explains things a little:
bool check2 = bb.GetType().FullName == "Two"; // well, you'd guess that
bool check3 = aa.GetType().FullName == "One[]"; // what?!
Seriously! The aa array is not Two[]. The array type has been "lost by variable" when it was cast to object, but both vals and (object)vals of course still refer to the same object. Then, after the following cast, it's still the aa object, the original array of One[], just hidden behind a new variable type.
The bb object/variable has type of Two because the array's item read as an item of a Two[] array (along with the variable's types). The real array One[] is concealed by the Two[] type, so indexing the Two[] must result in value of Two type.
Furthermore, since the actual type is hidden and since Enum types seem to be treated lightly, let's check another thing:
var numbers = (int[])(object)vals;
var cc = numbers[0] + 10; // == 10, from one0's value
var dd = numbers[1] + 10; // == 13, from one3's value
and similarly:
bool check4 = numbers.GetType().FullName == "One[]"; // not System.Int32[]
So, as you might already guess, the other way around is possible too:
var numbers2 = new int[]{ 0, 2, 99 };
var enums = (One[])(object)numbers2;
One ee = enums[0]; // == One.one0
One ff = enums[1]; // == One.one2
One gg = enums[2]; // == ((One)99)
and int[] also remembers its real type, even if casted to One[]:
bool check5 = numbers2.GetType().FullName == "System.Int32[]";
Even further, you cannot trust the as and is operators when the array is passed as object:
bool really_true1 = vals is One[];
bool really_true2 = vals is Two[];
bool really_true3 = vals is System.LoaderOptimization[];
This one was a 'gotha!' for me recently.
It actually reuses the original array object (instead of duplicating it like with LINQ) and it exposes it as different type - be it Two[] or int[]. It's seems to be a "true cast", no boxing. Much different than LINQ-driven copying&conversion.
int[] b = Array.ConvertAll((int[])Enum.GetValues(typeof(TestEnum)), Convert.ToInt32);
Related
I've read other similar Questions. But, All of them use bash. IDK anything about that language.
The Thing I want to do is:
int i=0; //Value of i will change as I want to use it in loop
string name="c"+i;
double a= name[i]; //The real name of arrays I have declared are: c0[],c1[] etc
It gives error: "Project" does not contain a definition for "name"
So, How do I acheive this?
Obviously you have a set of variables, all sharing a common name, e.g. MyVariable1, MyVariable2, etc.
Instead of having so many similar variables, you should use an array, or in your case an array of arrays:
var myVariableArray = new double[][] { c[0], c[1], ... };
Now you can easily acces the i-th number within that array:
double a = myVariableArray[i][i];
Alternativly if those variables actually are members (fields or propertiers) within your class, you can also use reflection to get the right member from a string:
var fields = typeof(MyType).GetField(name + i);
double b[];
if(field != null)
b = (double[]) field.GetValue(instanceOfMyType, null);
else
{
var prop = typeof(MyType).GetProperty(name + i);
if(prop != null)
b = (double[]) prop.GetValue(instanceOfMyType, null);
}
a = b[i];
However such a data-structure is bad design, you should go with an array (or list) of members, instead of having dozens of similar members.
You need an array of arrays (two-dimensional arrays), don't you?
To create an array of arrays, do this:
double[][] twoDArray = new double[][x];
where x is the number of arrays you want.
Now you can populate it with some arrays like this:
twoDArray[0] = new double[] {1.0, 1.1, 1.2};
twoDArray[1] = new double[] {7.7, 8.8, 9.9};
To access an array in the 2D array, you don't even need name, you just use i directly!
double[] oneOfTheArrays = twoDArray[i];
double a = oneOfTheArray[0];
Or more simply:
double a = twoDArray[i][0];
Is it possible to use except with two lists of int arrays, like so:
List<int[]> a = new List<int[]>(){ new int[]{3,4,5}, new int[]{7,8,9}, new int[]{10,11,12} };
List<int[]> b = new List<int[]>(){ new int[]{6,7,9}, new int[]{3,4,5}, new int[]{10,41,12} };
var c = a.Except(b);
and exepecting {3,4,5} to be absent of the enumerable c? Of course I tried and this one is not working. Is there a solution as efficient as Except? Or even better, faster?
In .NET, arrays are only equal to another if they are the exact same array object. So two distinct arrays which have the same contents are not considered equal:
int[] x = new int[] { 1, 2 };
int[] y = new int[] { 1, 2 };
Console.WriteLine(x == y); // false
In order to check the equality based on the contents, you can use Enumerable.SequenceEqual:
Console.WriteLine(x.SequenceEqual(y)); // true
Of course that doesn’t help you directly when trying to use Enumerable.Except, since by default that will use the default equality comparer which only checks for equality (and since every array is inequal to every other array except itself…).
So the solution would be to use the other overload, and provide a custom IEqualityComparer which compares the arrays based on their content.
public class IntArrayEqualityComparer : IEqualityComparer<int[]>
{
public bool Equals(int[] a, int[] b)
{
return a.SequenceEqual(b);
}
public int GetHashCode(int[] a)
{
return a.Sum();
}
}
Unfortunately, just delegating to SequenceEqual is not enough. We also have to provide a GetHashCode implementation for this to work. As a simple solution, we can use the sum of the numbers in the array here. Usually, we would want to provide a strong hash function, which tells a lot about the contents, but since we are only using this hash function for the Except call, we can use something simple here. (In general, we would also want to avoid creating a hash value from a mutable object)
And when using that equality comparer, we correctly filter out the duplicate arrays:
var c = a.Except(b, new IntArrayEqualityComparer());
That's because default EqualityComparer for int array returns false for to arrays with same values:
int[] a1 = { 1, 2, 3 };
int[] a2 = { 1, 2, 3 };
var ec = EqualityComparer<int[]>.Default;
Console.WriteLine(ec.Equals(a1, a2));//result is false
You can fix it by implementing your own EqualityComparer and passing its instance to Except method (see documentation).
You can also read about arrays comparison in C# here.
Is there anyway to obtain a value type by reference as opposed to by value from a collection of value types, similar to the way one can pass a value type by reference to a method?
var myInts = new List<int> { 1, 2, 3 };
var item1 = myInts[0];
item1 += 23;
Console.WriteLine(myInts[0]);
// ouputs: 1
myInts[0] += 987;
Console.WriteLine(myInts[0]);
// outputs: 988
So the modification to the item1 variable would be modifying the element in the collection too.
Using the indexer works as above because I am re-assigning the element after getting it.
As far as I know, this is not possible.
When you write
var item1 = myInts[0];
The value that is stored in the position with index 0 in the list called myInts is assigned to the item1 variable. Since this list is a list of value types, the value that is assigned to the item1 is the actual value of the element in zero position, it isn't a reference like it wold be if myInts were holding the instances of a reference type. So any modification to the value that is assigned to item1 will not result to a modification to the element myInts[0].
Actually, I don't see a reason why something like that you are asking would be useful. If you could point us a use case where this would be beneficial, I would be grateful to you.
No, that's not possible.
If you're asking about Int32, answer is no. If it is your own struct you can achieve it by "interfaces".
interface IMyInt
{
int Value{get;set;}
}
struct MyInt : IMyInt
{
public int Value { get; set; }
}
void Main()
{
var myInts = new List<IMyInt> { new MyInt{ Value = 1}, new MyInt{ Value = 2}, new MyInt{ Value = 3} };
var item1 = myInts[0];
item1.Value += 23;
Console.WriteLine(myInts[0].Value);
}
Prints
24
This works because myInts contains list of boxed structs which will be copied by reference.
You can play a little trickery with lambdas to get something that may work the way you want.
So if you are happy to pass around a reference to a Action<Func<int, int>> you can update the elements of a collection.
So given:
var myInts = new List<int> { 1, 2, 3 };
I can define a delegate like this:
Action<Func<int, int>> updateItem1 = f => myInts[0] = f(myInts[0]);
Now I can pass around Action<Func<int, int>> updateItem1 anywhere and use it like this:
updateItem1(x => x + 23);
updateItem1(x => x + 987);
And this updates the elements of the collection without passing around the collection.
Try it like this:
var myInts = new List<int> { 1, 2, 3 };
Action<Func<int, int>> updateItem1 =
f => myInts[0] = f(myInts[0]);
updateItem1(x => x + 23);
Console.WriteLine(myInts[0]); // 24
updateItem1(x => x + 987);
Console.WriteLine(myInts[0]); // 1011
In an array, field, or an assignable variable, you can do it, with ref keyword.
var myInts = new int[] { 1, 2, 3 };
ref var item1 = ref myInts[0];
item1 += 23;
Console.WriteLine(myInts[0]); // outputs: 24
myInts[0] += 987;
Console.WriteLine(myInts[0]); // outputs: 1011
Console.WriteLine(item1); // outputs: 1011
In a collection, you cannot. (At least, in a List you cannot; I just assume other collections are implemented the same). For root cause, you can see here:
https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,181
You can see the indexer returns return _items[index]; which means the return value of the List indexer is a temporary copy of the element.
// This does not compile. The error is "A property or indexer may not
// be passed as an out or ref parameter" or "Indexer access returns
// temporary value. 'ref' argument must be an assignable variable,
// field, or an array element."
var myInts2 = new List<int>() {1, 2, 3};
ref var item2 = ref myInts2[0];
Is it possible to do something like this:
string A = "A";
string B = "B";
object[] O = { A, B };
O[0] = "C";
Where A will hold the value "C" in the end?
The code above will replace O[0] with "C", but A remains unchanged.
No - at least not in safe code.
When you create the array, it copies the values into the array. Changing the value in the array later won't change the value of the variable. There's no way of creating a sort of "ref array", where the array elements are shared with the variables. (As shown in Mark's answer, there are sometimes ways of doing this in unsafe code, but I'd really suggest staying away from that.)
Note, however, that if both the array and the variable refer to the same mutable object then mutating that object via either path will make a change which is visible via the other:
StringBuilder x = new StringBuilder();
StringBuilder[] array = new StringBuilder[] { x };
array[0].Append("Foo");
Console.WriteLine(x); // Prints "Foo"
This is no different to any other assignment though - and note that in the code above, the Append call doesn't change the value of either x or the array element; it changes the data within the object that those values refer to.
While the answer is no in the case of string, you can do this with value types:
class Program
{
static unsafe void Main()
{
char A = 'A';
char B = 'B';
var O = new char*[] { &A, &B };
*O[0] = 'C';
System.Console.WriteLine(A + "," + B); // outputs C,B
}
}
Unsafe code is typically frowned upon in C#. So, while this kind of thing is possible, I wouldn't recommend doing it.
What you probably want is to convert your strings into objects with properties.
You can then Cast your array object on the specific key to this object type you created, and then you can set its property again.
This way you can change both what's in your array, as your original variable, therefor, it is similar to seeing it as an array with values by reference.
public class stringObject
{
private string name;
public string Name { get { return name; } set { name = value; } }
}
stringObject A = new stringObject();
A.Name = "A";
stringObject B = new stringObject();
B.Name = "B";
object[] O = { A, B };
//change the array at key 0, and also changing our original variable
stringObject C = O[0] as stringObject;
C.Name = "C";
The above code will not only change what is inside O[0], but it will also update what is inside your object A.
An example with a test to write to the console can be seen here:
https://dotnetfiddle.net/Yt25hy
Is it possible to concat two list that are of different types?
string[] left = { "A", "B", "C" };
int[] right = { 1, 2, 3 };
var result = left.Concat(right);
The code above obciously has a type error. It works if the types match (eg. both are ints or strings).
pom
You can box it.
var result = left.Cast<object>().Concat(right.Cast<object>());
result will be IEnumerable<object>.
Then to unbox it, you can use OfType<T>().
var myStrings = result.OfType<string>();
var myInts = result.OfType<int>();
You could cast both to a common base type (in this case object), or you could convert the types of one of the lists so you can combine them, either:
right.Select(i = i.ToString())
or
left.Select(s => int.Parse(s)) // Careful of exceptions here!
Only way I know to make this work would be:
var result = left.Concat(right.Select(i = i.ToString()));