Say I have an array of fixed size N, is there a way to map the elements to a list of N variable names?
I was thinking of something like:
variable1, variable2, variable3 = arrayOfSize3;
EDIT:
A few people have remarked that this would be useless and suggested that I am doing something wrong. Maybe I am, but this is a pretty common feature in dynamic languages so I was hoping C# had something elegant as an alternative.
If it helps, I can write what I need it for. I have parsed an HTML table and have an array of strings representing a row of the table. I made a class to represent the row with variables representing the data, but to store the data I have to manually set the names to each of the array elements. I know there are other ways to do this, but I was wondering more out of curiosity than anything else, what is the right way to map variables like this?
Their is no such assignment, but you can do like this:
var variable1 = arrayOfSize3[0];
var variable2 = arrayOfSize3[1];
var variable3 = arrayOfSize3[2];
Not production ready yet, but you can do this in C# 7 preview which comes with Visual Studio 15 preview. Using deconstruction matching, the following code works with Tuple
var (variable1, variable2, variable3) = tupleOfSize3;
This feature actually works with anything with a deconstructor method like this
public void Deconstruct(out T1 x1, ..., out Tn xn) { ... }
Maybe array will have this extension. Please see What's new in C# 7.0
Tuples are a set of a fixed number values, each with its own name and type. Arrays, on the other hand, are a set of a variable number of anonymous values, all of the same type. These are two very distinct forms of aggregate types, and they really serve mostly disjoint use cases.
Having said that, it is possible to deconstruct an array (in C# 7 preview which comes with Visual Studio 15 preview)! You can do it by adding your own Deconstruct method as an extension method:
class C
{
public static void Main()
{
int[] x = new int[2];
var (a, b) = x;
}
}
static class ArrayUtilities
{
public static void Deconstruct(this int[] data, out int a, out int b)
{
a = data[0];
b = data[1];
}
}
Related
If I have a method parameter that is an enum, intellisense will pick up the possible values for this enum and let me pick one. This isn't ideal for me however as it's possible people might want to use values outside of my defined set. If I make my argument a byte instead, I can then create a static class filled with consts that hold my defined set of values - the only downside is that intellisense does not know about this library of values. Is there a way to point intellisense towards a range of 'helper' values?
Technically you can assign 'invalid' values to your enum. Since the backing store of an enum is an int, you can assign any value to it:
public enum X
{
A = 0,
B = 1
}
class Program
{
static void Main(string[] args)
{
X x = (X)2;
}
}
That way, you can still have the IntelliSense support, and allow off-values. Of course, this has drawbacks too, so you have to consider whether they outweigh the pros.
A fix for that could be to assign 'custom' values in your enum, which you reserve for use later on:
public enum X
{
A = 0,
B = 1,
Custom1 = 2
}
To directly answer the Intellisense part of you question, then no I don't think it is possible to do that.
However I think you can solve your problem by using function overloading, this way you can use either type and have the benefits of both:
void Myfunction(MyEnum e)
{
MyFunction((byte)e);
}
void MyFunction(byte b)
{
// Do something
}
I decompiled some C# 7 libraries and saw ValueTuple generics being used. What are ValueTuples and why not Tuple instead?
https://learn.microsoft.com/en-gb/dotnet/api/system.tuple
https://learn.microsoft.com/en-gb/dotnet/api/system.valuetuple
What are ValueTuples and why not Tuple instead?
A ValueTuple is a struct which reflects a tuple, same as the original System.Tuple class.
The main difference between Tuple and ValueTuple are:
System.ValueTuple is a value type (struct), while System.Tuple is a reference type (class). This is meaningful when talking about allocations and GC pressure.
System.ValueTuple isn't only a struct, it's a mutable one, and one has to be careful when using them as such. Think what happens when a class holds a System.ValueTuple as a field.
System.ValueTuple exposes its items via fields instead of properties.
Until C# 7, using tuples wasn't very convenient. Their field names are Item1, Item2, etc, and the language hadn't supplied syntax sugar for them like most other languages do (Python, Scala).
When the .NET language design team decided to incorporate tuples and add syntax sugar to them at the language level an important factor was performance. With ValueTuple being a value type, you can avoid GC pressure when using them because (as an implementation detail) they'll be allocated on the stack.
Additionally, a struct gets automatic (shallow) equality semantics by the runtime, where a class doesn't. Although the design team made sure there will be an even more optimized equality for tuples, hence implemented a custom equality for it.
Here is a paragraph from the design notes of Tuples:
Struct or Class:
As mentioned, I propose to make tuple types structs rather than
classes, so that no allocation penalty is associated with them. They
should be as lightweight as possible.
Arguably, structs can end up being more costly, because assignment
copies a bigger value. So if they are assigned a lot more than they
are created, then structs would be a bad choice.
In their very motivation, though, tuples are ephemeral. You would use
them when the parts are more important than the whole. So the common
pattern would be to construct, return and immediately deconstruct
them. In this situation structs are clearly preferable.
Structs also have a number of other benefits, which will become
obvious in the following.
Examples:
You can easily see that working with System.Tuple becomes ambiguous very quickly. For example, say we have a method which calculates a sum and a count of a List<Int>:
public Tuple<int, int> DoStuff(IEnumerable<int> values)
{
var sum = 0;
var count = 0;
foreach (var value in values) { sum += value; count++; }
return new Tuple(sum, count);
}
On the receiving end, we end up with:
Tuple<int, int> result = DoStuff(Enumerable.Range(0, 10));
// What is Item1 and what is Item2?
// Which one is the sum and which is the count?
Console.WriteLine(result.Item1);
Console.WriteLine(result.Item2);
The way you can deconstruct value tuples into named arguments is the real power of the feature:
public (int sum, int count) DoStuff(IEnumerable<int> values)
{
var res = (sum: 0, count: 0);
foreach (var value in values) { res.sum += value; res.count++; }
return res;
}
And on the receiving end:
var result = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {result.sum}, Count: {result.count}");
Or:
var (sum, count) = DoStuff(Enumerable.Range(0, 10));
Console.WriteLine($"Sum: {sum}, Count: {count}");
Compiler goodies:
If we look under the cover of our previous example, we can see exactly how the compiler is interpreting ValueTuple when we ask it to deconstruct:
[return: TupleElementNames(new string[] {
"sum",
"count"
})]
public ValueTuple<int, int> DoStuff(IEnumerable<int> values)
{
ValueTuple<int, int> result;
result..ctor(0, 0);
foreach (int current in values)
{
result.Item1 += current;
result.Item2++;
}
return result;
}
public void Foo()
{
ValueTuple<int, int> expr_0E = this.DoStuff(Enumerable.Range(0, 10));
int item = expr_0E.Item1;
int arg_1A_0 = expr_0E.Item2;
}
Internally, the compiled code utilizes Item1 and Item2, but all of this is abstracted away from us since we work with a decomposed tuple. A tuple with named arguments gets annotated with the TupleElementNamesAttribute. If we use a single fresh variable instead of decomposing, we get:
public void Foo()
{
ValueTuple<int, int> valueTuple = this.DoStuff(Enumerable.Range(0, 10));
Console.WriteLine(string.Format("Sum: {0}, Count: {1})", valueTuple.Item1, valueTuple.Item2));
}
Note that the compiler still has to make some magic happen (via the attribute) when we debug our application, as it would be odd to see Item1, Item2.
The difference between Tuple and ValueTuple is that Tuple is a reference type and ValueTuple is a value type. The latter is desirable because changes to the language in C# 7 have tuples being used much more frequently, but allocating a new object on the heap for every tuple is a performance concern, particularly when it's unnecessary.
However, in C# 7, the idea is that you never have to explicitly use either type because of the syntax sugar being added for tuple use. For example, in C# 6, if you wanted to use a tuple to return a value, you would have to do the following:
public Tuple<string, int> GetValues()
{
// ...
return new Tuple(stringVal, intVal);
}
var value = GetValues();
string s = value.Item1;
However, in C# 7, you can use this:
public (string, int) GetValues()
{
// ...
return (stringVal, intVal);
}
var value = GetValues();
string s = value.Item1;
You can even go a step further and give the values names:
public (string S, int I) GetValues()
{
// ...
return (stringVal, intVal);
}
var value = GetValues();
string s = value.S;
... Or deconstruct the tuple entirely:
public (string S, int I) GetValues()
{
// ...
return (stringVal, intVal);
}
var (S, I) = GetValues();
string s = S;
Tuples weren't often used in C# pre-7 because they were cumbersome and verbose, and only really used in cases where building a data class/struct for just a single instance of work would be more trouble than it was worth. But in C# 7, tuples have language-level support now, so using them is much cleaner and more useful.
I looked at the source for both Tuple and ValueTuple. The difference is that Tuple is a class and ValueTuple is a struct that implements IEquatable.
That means that Tuple == Tuple will return false if they are not the same instance, but ValueTuple == ValueTuple will return true if they are of the same type and Equals returns true for each of the values they contain.
In addition to the comments above, one unfortunate gotcha of ValueTuple is that, as a value type, the named arguments get erased when compiled to IL, so they're not available for serialisation at runtime.
i.e. Your sweet named arguments will still end up as "Item1", "Item2", etc. when serialised via e.g. Json.NET.
Other answers forgot to mention important points.Instead of rephrasing, I'm gonna reference the XML documentation from source code:
The ValueTuple types (from arity 0 to 8) comprise the runtime implementation that underlies
tuples in C# and struct tuples in F#.
Aside from created via language syntax, they are most easily created via the
ValueTuple.Create factory methods.
The System.ValueTuple types differ from the System.Tuple types in that:
they are structs rather than classes,
they are mutable rather than readonly, and
their members (such as Item1, Item2, etc) are fields rather than properties.
With introduction of this type and C# 7.0 compiler, you can easily write
(int, string) idAndName = (1, "John");
And return two values from a method:
private (int, string) GetIdAndName()
{
//.....
return (id, name);
}
Contrary to System.Tuple you can update its members (Mutable) because they are public read-write Fields that can be given meaningful names:
(int id, string name) idAndName = (1, "John");
idAndName.name = "New Name";
Late-joining to add a quick clarification on these two factoids:
they are structs rather than classes
they are mutable rather than readonly
One would think that changing value-tuples en-masse would be straightforward:
foreach (var x in listOfValueTuples) { x.Foo = 103; } // wont even compile because x is a value (struct) not a variable
var d = listOfValueTuples[0].Foo;
Someone might try to workaround this like so:
// initially *.Foo = 10 for all items
listOfValueTuples.Select(x => x.Foo = 103);
var d = listOfValueTuples[0].Foo; // 'd' should be 103 right? wrong! it is '10'
The reason for this quirky behavior is that the value-tuples are exactly value-based (structs) and thus the .Select(...) call works on cloned-structs rather than on the originals. To resolve this we must resort to:
// initially *.Foo = 10 for all items
listOfValueTuples = listOfValueTuples
.Select(x => {
x.Foo = 103;
return x;
})
.ToList();
var d = listOfValueTuples[0].Foo; // 'd' is now 103 indeed
Alternatively of course one might try the straightforward approach:
for (var i = 0; i < listOfValueTuples.Length; i++) {
listOfValueTuples[i].Foo = 103; //this works just fine
// another alternative approach:
//
// var x = listOfValueTuples[i];
// x.Foo = 103;
// listOfValueTuples[i] = x; //<-- vital for this alternative approach to work if you omit this changes wont be saved to the original list
}
var d = listOfValueTuples[0].Foo; // 'd' is now 103 indeed
Hope this helps someone struggling to make heads of tails out of list-hosted value-tuples.
My background is all in dynamic languages for many years, so reasoning about how static typed languages get data from JSON where the key/values can be different types is becoming an issue that really has me stuck. Here's an example of the data I have:
{
"par": 4,
"level": [0,1,0,1,1, 0,1,0,1,1, 0,1,0,1,1, 0,1,0,1,1, 0,1,0,1,1]
}
And I want to get this into some local variables like these
int par;
List<bool> levelData;
I'm using MiniJSON.cs to do the parsing, and it gives me back Dictionary<string, object> but I can't figure out how to get the par and level values from that and convert them to the different types they actually represent.
The problem with trying to convert to List<int> was that MiniJSON parses integers into Int64 and int is 32-bit, so the conversion won't be allowed implicitly because of data loss. Converting to a list of Int64 worked!
List<Int64> cells = (List<Int64>)data["level"];
You have to provide a "decoder" that transforms the dictionary into a typed data structure. This is especially obvious since you are using 0 and 1 to represent boolean values.
Suppose the class SomeClass holds your typed data. The decoder would have to do something like this:
SomeClass decode(Dictionary<string,object> d) {
var m = new SomeClass();
m.par = (int)d["par"];
m.levelData = ((List)d["level"]).Select(x => (int)x == 0 ? false : true).ToList();
return m;
}
So, basically you must turn the (untyped) objects into typed values, and you have to do that explicitly. Note that here it is assumed that your list of zeroes and ones is homogeneous (i.e. does not contain "strange" values like strings, which would be perfectly legal in a dynamically typed language).
My environment is: W7 Ultimate 64 bits, VS2010 Ultimate, C#, WinForm, target XP and W7.
With the help of #dasblinkenlight, the concatenation on the for loop was very good.
I feel we are making great progress.
As you ca see, we are putting into the array sMntHour[d,h] the string "csv_001_01" if d=1 and h=1 and so on.
This csv_001_01, csv_001_02,.. ; are variables that contains an integer value.
csv_001_01=5111;
csv_001_02=236; // This is a sample, because has 365 days in normal year
// and 366 days in leaf year. "csv_day_hour"
Directly we could do this:
sMntHour[d,h] = csv_001_01.ToString(); // d is day and h is hour
sMntHour[d,h] = csv_001_02.ToString();
As we put the value of this concatenated variable in the array and not the name of the variables?
for(int d=1;d<=365;d++) //I'll put the code to not leap years.
{
for(int h=1; h<=24; h++)
{
sMntHour[d,h] = string.Format("csv_{0:000}_{1:00}", d, h)
}
}
If I understand what you mean, you have all the variable names and now you want to get their values.
You could do this using Reflection and you can create a dictionary where keys are the variable names and the values are the actual values. It is really hard to help without seeing how these variables are declared, are they fields / properties ? are the private, static ? etc... But something like this should work, in theory:
var type = this.GetType();
var values = sMntHour.OfType<string>()
.ToDictionary(
x => x,
x => (int)type.GetField(x).GetValue(this));
Then you can access the values using values["variable_name"]
Or if you don't want this, instead if you want to access them using index like [d,h] as mentioned in comments, do not store the variable names in the first place instead store the values in your array:
var type = this.GetType();
for(int d=1;d<=365;d++)
{
for(int h=1; h<=24; h++)
{
var name = string.Format("csv_{0:000}_{1:00}", d, h);
sMntHour[d,h] = (int)type.GetField(name).GetValue(this);
}
}
Ofcourse you need to change the type of sMntHour, in order to make it work.
I was able to create an array of a struct I created, but I'm having trouble doing the same for an array of a class. I'm (faintly) aware that this probably isn't the best way to do this, but I'd appreciate help in figuring out what's going on.
I'm about 2 days into learning C#, and I'm navigating away from MS Office-VBA, if that gives you an idea of what I'm into. Anyway, I'm following an online reference, and along the way trying to play with what I've learned so far. This problem has come about as a result of my playing.
First, let me describe what I've done with the struct, and the array of that struct, with some code snippets.
I've been able to create a struct, called Machines...
// play with structs
struct Machines
{
// vars for struct
private string model, SN;
private int hours;
// assign values
public void AssignValues(string model_in, string SN_in, int hours_in)
{
model = model_in;
SN = SN_in;
hours = hours_in;
}
// display values
public void DisplayValues()
{
Console.WriteLine("Model: {0}", model);
Console.WriteLine("SN: {0}", SN);
Console.WriteLine("Hours: {0}", hours);
}
};
... things seem to work just fine:
public static void Main()
{
// play with structures
Machines machine1 = new Machines();
machine1.AssignValues("AA", "ABC01234", 34760);
machine1.DisplayValues();
Output is:
Model: AA
SN: ABC01234
Hours: 34760
Then, I can create an array of the struct, and things continue to go well:
// play with structures and arrays
// declare, create new instance
Machines [] MyArr = new Machines[10];
MyArr[0].AssignValues("AA", "ABC01235", 43000);
MyArr[0].DisplayValues();
But, when I attempt to do the same with a class, it's a different story. What's going on?
public class ArmstrongMachine
{
// vars for struct
private string model, SN;
private int hours;
// assign values
public void AssignValues(string model_in, string SN_in, int hours_in)
{
model = model_in;
SN = SN_in;
hours = hours_in;
}
// display values
public void DisplayValues()
{
Console.WriteLine("Model: {0}", model);
Console.WriteLine("SN: {0}", SN);
Console.WriteLine("Hours: {0}", hours);
}
};
...
// play with classes
ArmstrongMachine [] MyMachines = new ArmstrongMachine[10];
MyMachines[0].AssignValues("AA", "ABC01236", 51000);
MyMachines[0].DisplayValues();
The issue seems to begin with MyMachines[0].AssignValues.... If I comment out that line and the following, there are no problems (other than the warning that I've created a variable I'm not using).
Any ideas?
Also, please be aware that this is being compiled online.
A class gives you a reference type in C#.
The array thus holds references to objects, and not the objects themselves.
The array initially contains nothing, all zeroes, which means all the references will be null.
You need to initialize each element of the array to hold an object reference:
// play with classes
ArmstrongMachine [] MyMachines = new ArmstrongMachine[10];
MyMachines[0] = new ArmstrongMachine();
MyMachines[0].AssignValues("AA", "ABC01236", 51000);
MyMachines[0].DisplayValues();
MyMachines[1] = new ArmstrongMachine();
MyMachines[2] = new ArmstrongMachine();
...
MyMachines[9] = new ArmstrongMachine();
If the array holds value types, like the structs, then the array holds the struct values themselves, thus it works with the structs, and not with the objects.
Also note that you should emphatically not use mutable structs (structs you can change). There's tons of things that can go wrong and bite you in ... so you should not use them. Go with classes in this case.
Here's a video on the subject of mutable structs: Evil Structs, by Jon Skeet.
In simple words: A struct is only a way the memory is structured whereas a class is a real object. For the second example, you need to create instances of the class before you can make calls to it, because it only points to a memory location (which may be unassigned = a null reference):
ArmstrongMachine [] MyMachines = new ArmstrongMachine[10];
MyMachines[0] = new ArmstrongMachine();
MyMachines[0].AssignValues("AA", "ABC01236", 51000);
MyMachines[0].DisplayValues();