Better writing of a static Dictionary - c#

I am keeping a static dictionary to map a simple integer stored in a database to an enum value.
static Dictionary<long, EModelType> AttributeIdTypeToEModelType =
new Dictionary<long, EModelType>()
{
{1, EModelType.StatStr},
{6, EModelType.HistStr},
{7, EModelType.HistVal}
};
The great advantage is that I use it to directly get my enum value as the data arrive from the database.
typ = AttributeIdTypeToEModelType[i];
The usage of this system is neat, but it doesn't look clean to me to have a static dictionary just for this.
I had no chance trying to find a cleaner way to use enumerators and overriding their values.
Any advice?

You don't need a Dictionary to cast an int to the appropriate Enum type:
var type = (EModelType)yourInt;
or using Enum.ToObject:
var type = Enum.ToObject(typeof(EModelType) , yourInt);
and you can check that it exists with Enum.IsDefined:
if (! Enum.IsDefined(typeof(EModelType), yourInt)) throw new ArgumentException("Illegal type");

If the value in the database is a string, then use:
EnumType x = (EnumType)Enum.Parse(typeof(EnumType), dr[0].ToString());
If the value in the database is a long, then use:
EnumType x = (EnumType)dr[0];
If you need the long value of an enum, use:
long x = (long)EnumType.SomeEnum

You should define your enum with an int backing type, like this:
public enum EModelType : int
{
StatStr = 1,
HistStr = 6,
HistVal = 7
}
Then, like Tim and Chris suggested, just cast or parse the integer from the database to your enum type.

Related

Why the tuple-type list element's value cannot be modified?

In C# 8.0, I can modify the value inside a tuple directly by accessing the field name:
(string name, int score) student = ("Tom", 100);
student.name = "Jack";
Console.WriteLine(student);
And I can modify the list element's property as follow:
var list = new List<Student>(); // assume I have a Student class which has a Name property
list.Add(new Student { Name = "Tom" });
list[0].Name = "Jack";
Console.WriteLine(list[0]);
But why can't I modify the tuple-type element's value like this?
var list = new List<(string name, int score)>();
list.Add(("Tom", 100));
list[0].name = "Jack"; // Error!
Console.WriteLine(list[0]);
A tuple (ValueTuple) is a struct. Rather than returning a reference to the value as is the case with your Student example, you would actually recieve a copy of the tuple.
Changes to that copy wouldn't be reflected in the list and would be discarded. The compiler is smart enough to recognize this and stops you from doing it.
If it did compile, it would be to something similar to the following:
var list = new List<(string name, int score)>(); list.Add(("Tom", 100));
var copy = list[0];
copy.name = "Jack";
Console.WriteLine(copy.name); // Jack
Console.WriteLine(list[0].name); // still Tom
Mutable structs can be dangerous if you don't use them properly. The compiler is simply doing its job.
You can work around this with the following:
var list = new List<(string name, int score)>(); list.Add(("Tom", 100));
var copy = list[0];
copy.name = "Jack";
list[0] = copy; // put it back
Console.WriteLine(copy.name); // Jack
Console.WriteLine(list[0].name); // Jack
Try It Online
If you use an array (string, int)[] instead of a List<(string, int)>, this isn't a problem due to the way array element access works:
var arr = new (string name, int score) [] { ( "Tom", 10 ) };
arr[0].name = "Jack";
Console.WriteLine(arr[0].name); // Jack
Try It Online
This behavior is not unique to List or your tuple type. You'll experience this issue with any collection where the element is a Value Type (unless of course they offer a ref element accessor).
Note that there are similar issues when having a readonly field of a mutable value type that mutates via method calls. This can be much more insidious as no error or warning is emitted:
struct MutableStruct {
public int Val;
public void Mutate(int newVal) {
Val = newVal;
}
}
class Test {
private readonly MutableStruct msReadonly;
private MutableStruct msNormal;
public Test() {
msNormal = msReadonly = new MutableStruct(){ Val=5 };
}
public void MutateReadonly() {
Console.WriteLine(msReadonly.Val); // 5
msReadonly.Mutate(66); // defensive copy!
Console.WriteLine(msReadonly.Val); // still 5!!!
}
public void MutateNormal() {
Console.WriteLine(msNormal.Val); // 5
msNormal.Mutate(66);
Console.WriteLine(msNormal.Val); // 66
}
}
new Test().MutateReadonly();
new Test().MutateNormal();
Try It Online
ValueTuple is a great addition to the framework and language. But there's a reason you'll often hear that [Mutable] structs are evil. In the majority of cases you shouldn't hit these restrictions. If you find yourself falling into this pattern a lot, I suggest moving over to a record, which is a reference type (thus not suffering these issues) and can be reduced to a tuple-like syntax.
Mutable value types are evil, it's hard to see why this prints "Tom" not "Jack":
(string name, int score) student = ("Tom", 100);
(string name, int score) student2 = student;
student.name = "Jack";
Console.WriteLine(student2);
The reason is that you always create a copy. Because it's not obvious you should avoid mutable value types. To avoid that people will fall into that trap the compiler just allows to modify the object directly via properties(like above). But if you try to do it via a method call you get a compiler error "Cannot modify the return value of ... because it is not a variable".
So this is not allowed:
list[0].name = "Jack";
It would create a new copy of the ValueTuple, assigns a value but doesn't use or store it anywhere.
This compiles because you assign it to a new variable and modify it via property:
(string name, int score) x = list[0];
x.name = "Jack"; // Compiles
So it compiles but gives you again a suprising result:
Console.WriteLine(list[0]); // Tom
Read more about it here: Do Not Define Mutable Value Types

Create Tuple Using List of datatype of string in c#

I need to create Tuple from list of datatype in string, but did not get any solution.
Here is example of what I want to do.
string[] dataType = {"int", "float", "datetime"};
//I want to create list like this but dynamically datatype come from db.
//List<Tuple<int, string, string>> list = new List<Tuple<int, string, string>>();
List<Tuple<dataType[0],dataType[1], dataType[2]>> list = new List<Tuple<dataType[0],dataType[1], dataType[2]>>();
//Here datatype value is in string so I want to convert string to actual datatype.
Or if any alternative solution for this please guide me.
This is to extend my comments under question, and show you what I meant there.
It is not hard to create a list of Tuple dynamically from list of types in string format. For example, use reflection:
private Type InferType(string typeName)
{
switch (typeName.ToLowerInvariant())
{
case "int":
return typeof(int);
case "float":
return typeof(float);
default:
return Type.GetType($"System.{typeName}", true, true);
}
}
private object CreateListOfTupleFromTypes(string[] types)
{
var elementTypes = types.Select(InferType).ToArray();
// Get Tuple<,,>
var tupleDef = typeof(Tuple)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.First(mi => mi.Name == "Create"
&& mi.GetParameters().Count() == elementTypes.Length)
.ReturnType
.GetGenericTypeDefinition();
// Get Tuple<int, float, DateTime>
var tupleType = tupleDef.MakeGenericType(elementTypes);
// Get List<Tuple<int, float, DateTime>>
var listType = typeof(List<>).MakeGenericType(tupleType);
// Create list of tuple.
var list = Activator.CreateInstance(listType);
return list;
}
The problem is because the list is created using types only known at runtime, in your code, you can never use the list as strong typed list. i.e.List<Tuple<int, float, DateTime>>.
Of course, you could make life easier when the list is used in your code by using ITuple:
var list = new List<ITuple>();
list.Add(new Tuple<int, float, DateTime>(...);
int value1 = (int)list[0][0];
float value1 = (float)list[0][1];
DateTime value1 = (DateTime)list[0][2];
However, if you do that, then there is no point to use Tuple. You only need List<object[]>.
So, this comes back to my question, what is the list of tuple for in your code?
You could use dynamic here for your types. For example, when you read the three values from your database you could assign them without worrying about the types like so:
dynamic a = 10;
dynamic b = "Some String";
dynamic c = new DateTime(2020,4,9);
var test = new System.Tuple<dynamic,dynamic,dynamic>(a, b, c);
It would depend on what you want to do with the values later on.
To create a list of those tuples use:
var list = new List<System.Tuple<dynamic,dynamic,dynamic>>();
You can get the type of the variable, for instance a with
a.GetType().ToString();
I am not sure if you can set a variable type based on a string value, though (the second part of your question).

C# 7 and Tuple casting through event aggregator

In C# 7 tuples became powerful:
var tuple = (speed: 45, color: "red");
Coupled with an event aggregator:
EvtAgreg.Publish("SomeInfo", (
speed: 45,
color: "red",
));
That would be very powerful to make decoupled code. However, from what I've gathered it seems impossible to recast that.
EvtAgreg.Subscribe("SomeInfo", this.GetType().Name, (obj) => {
var data = ((int, string)) obj;
}
However, this is not flexible and was wondering if there might be another way to cast on runtime similar to JS.
EvtAgreg.Subscribe("SomeInfo", this.GetType().Name, (obj) => {
var data = (?) obj;
data.speed ... or data["speed"]
}
Obviously that isn't possible, however, would it be through a class holding the tuple and its format (Type) ?
Edit: Trying to bring clarity about the question.
It is not about casting, but how to make the casting simple.
The point is, if you have a complex dataset of say 20 values that can be of different types, the casting becomes something like
var x = ((string, int, int, int, float, string, string, int, int, int, string, int, int, int, float, string, string, int, int, int)) obj;
Which becomes impossible to maintain.
Here is a way to achieve something similar with Hashtable:
Publisher:
MessageManager.Publish("Hardware_NewIRInfos",
new Hashtable() {
{ "position", i },
{ "status", (int)Load.IRStatus(i) },
{ "serial", (int)Load.IRSerial(i) },
{ "id", (int)Load.IRID(i) },
{ "version", Load.IRVersion(i) },
{ "cal", (int)Load.IRCalDate(i) },
{ "emissivity", Load.IREmissivity(i) },
{ "transmissivity",Load.IRTransmissivity(i) },
{ "gain", Load.IRGain(i) },
{ "offset", Load.IROffset(i) }
});
Subscriber:
MessageManager.Subscribe("Hardware_NewIRInfos", this.GetType().Name, (obj) =>
{
Hashtable data = (Hashtable)obj;
IRSensorParameters[pos ].Channel = pos;
IRSensorParameters[pos ].Status = (int) data["status"];
IRSensorParameters[pos ].ID = (int)data["id"]; ;
IRSensorParameters[pos ].Serial = (int) data["serial"];
IRSensorParameters[pos ].Version = (string)data["version"];
IRSensorParameters[pos ].CalibrationDate = (int) data["cal"];
IRSensorParameters[pos ].Emissivity = (float)data["emissivity"];
IRSensorParameters[pos ].Transmissivity = (float)data["transmissivity"];
IRSensorParameters[pos ].Gain = (float)data["gain"];
IRSensorParameters[pos ].Offset = (float)data["offset"];
});
In here you still have static casting, but it is much easier to maintain.
Is it possible to have a similar, better way, using C# 7 Tuples?
You are missing one set of brackets:
var data = ((int, string)) obj;
Think of it like this:
to cast you write (MyType)obj so casting requires set of brackets,
to declare tuple you wirite (int, string) so it requires its own set of brackets,
it cannot be the same set of brackets so you get double.
as long as your inner types match you can cast to whatever named properties you want:
var a = ((int speed, string color))(3,"red");
var c = ((int someInt, string someString))a;
var color = c.someString;
Also you can always use generic names Item1 Item2 they are not hinted by intellisense but are valid. There is no (easy) way to convert it to array but you have constant there either way so this should be fine.
One more nice thing is tuple expansion you can just unload it to variables in one line:
var tuple =(speed: 4, color: "red");
var (number, text) = tuple;
So yes tuples become hard to wrap you head around fast. And I would use them only for private in class communication between methods just because it does not require formal type declaration. Your use of tuple with 20 items is just wrong. Do not do that. In my opinion 4 items in tuple is too much and warrant dedicated type creation. If you really want such very fragile setup you can use your code exactly as you have written Hashtable is valid c# class. I would just create dedicated type.
So the bottom line is no you cannot have this using tuple just create dedicated class or use already defined type - whatever is in IRSensorParameters[pos]. To avoid dependency you can send string that contains your data eg. in json format.

cast list of anonymous type to list of object

I am trying to create a anonymous list which can hold any data type with intellisense support but without creating a class.
So I found below solution to use anonymous type.
var list = new[]
{
new { Number = 10, Name = "Smith" },
new { Number = 10, Name = "John" }
}.ToList();
foreach (var item in list)
{
Console.WriteLine(item.Name);
}
But what if I want a method which returns above anonymous type.
public List<object> GetData()
{
var list = new[]
{
new { Number = 10, Name = "Smith" },
new { Number = 10, Name = "John" }
}.ToList();
return list;
}
Compile Time Error:
Cannot implicitly convert type
'System.Collections.Generic.List<>' to 'System.Collections.Generic.List
Is it possible to cast list of anonymous type to list of object with intellisense support ?
Update:
The reason we don't want to create a type , because we just want to do some data manipulation using the anonymous type and populate some other objects that's all.
Here are the ways possible:
Return a List<object>, which means you have no intellisense on the receiving end
Return a List<dynamic>, which means you have no intellisense on the receiving end, but perhaps easier to access members you know are there than through reflection
Return a List<T> but then you will have to provide an example of how T is supposed to look and this won't be any more safe at runtime than dynamic is
Return a tuple of the new type that came with C# 7
Point 1 and 2 can be easily solved by just ensuring the list is of that type:
...
}.ToList<object>();
Point 3 is a hack but I'll post it below.
Point 4 can be solved with this syntax:
public List<(int Number, string Name)> GetData()
{
var list = new[]
{
(Number: 10, Name: "Smith"),
(Number: 10, Name: "John")
}.ToList();
return list;
}
This will give you intellisense for a while but the naming of the properties is a hack by the compiler and if you start passing these values around they will easily fall back to .Item1 and .Item2.
To cast an object to a specific type you can use a hack which only works in the same assembly that the anonymous object was created in, and that is that multiple anonymous types used around in your code, which has the same properties, in the same order, with the same property types, all end up being the same anonymous type.
You can thus cast an object to a specific anonymous type with this hackish code:
public T AnonymousCast<T>(object value, T example) => (T)value;
public IEnumerable<T> AnonymousCastAll<T>(IEnumerable<object> collection, T example) => collection.OfType<T>();
You would use it in your case like this:
var d = AnonymousCast(GetData()[0], new { Number = 0, Name = "" });
This is no more safe than using dynamic as there is no guarantee the object returned from GetData actually is of that anonymous type.
In short, use a named type.

Cannot implicitly convert type System.Collections.Generic.Dictionary<System.Tuple<int,int,string>, AnonymousType#1>

I have the following dictionary in a method:
var nmDict = xelem.Descendants(plantNS + "Month").ToDictionary(
k => new Tuple<int, int, string>(int.Parse(k.Ancestors(plantNS + "Year").First().Attribute("Year").Value), Int32.Parse(k.Attribute("Month1").Value), k.Ancestors(plantNS + "Report").First().Attribute("Location").Value.ToString()),
v => {
var detail = v.Descendants(plantNS + "Details").First();
return
new
{
BaseHours = detail.Attribute("BaseHours").Value,
OvertimeHours = detail.Attribute("OvertimeHours").Value
};
});
I need to return nmDict. The problem is that I cannot figure out how to label my method signature. I have tried the following:
protected IDictionary<XElement, XElement> OvertimereportData(HarvestTargetTimeRangeUTC ranges)
The above gives me this error:
Cannot implicitly convert type System.Collections.Generic.Dictionary<System.Tuple<int,int,string>,AnonymousType#1>' to 'System.Collections.Generic.IDictionary<System.Xml.Linq.XElement,System.Xml.Linq.XElement>'. An explicit conversion exists (are you missing a cast?)
protected IDictionary<Tuple, XElement> OvertimereportData(HarvestTargetTimeRangeUTC ranges)
gives me this error:
'System.Tuple': static types cannot be used as type arguments
I do not know what to do.
The short answer: You can't return anonymous types from a function.
The long answer: Your dictionary's value type is anonymous {BaseHours, OvertimeHours} which cannot be returned from a function or passed as an argument (except as an object, but that does nobody any good unless you go through the hassle of reflecting into it). Either define a class/struct with BaseHours and OvertimeHours in it, or use a tuple. The former is probably slightly better because you can keep the names BaseHours and OvertimeHours; with a tuple you just get Value1 and Value2.
If you are using C# 4.0 than you can return the anonymous via dynamic type. So your method signature would look like this
protected IDictionary<Tuple<int,int,string>, dynamic> OvertimereportData(HarvestTargetTimeRangeUTC ranges)
And through the dynamic object you can find the properties at run time.
Hope this will help you.
When you call the ToDictionary method, the resulting dictionary's type has little to do with the type of elements in your source sequence. It's defined entirely by the data types returned by the key and value expressions you supply to the call. For example, if you were to call:
xelem.Descendants(plantNS + "Month").ToDictionary(
k => int.Parse(k.Attribute("Year").Value),
v => k.Attribute("Year).Value
);
You would get an IDictionary<int, string> because that's what your two expressions returned. To return that from a method, you just need to construct the correct type, based on your expressions.
Your first one is easy:
k => new Tuple<int, int, string>(...)
The second one, though, is going to be a problem. The values in your dictionary are of an anonymous type: you return a new { } without specifying a concrete type name for that value. In general, that is going to make it impossible for you to use that dictionary as a return value or parameter. (It can be done, using some very strange-looking generic techniques, but I wouldn't recommend it.)
The first thing you'll need to do, then, is make a concrete type to hold your values, e.g.
public class HoursContainer
{
public string BaseHours { get; set; }
public string OvertimeHouse { get; set; }
}
and change your Linq query appropriately:
var detail = v.Descendants(plantNS + "Details").First();
return new HoursContainer
{
BaseHours = detail.Attribute("BaseHours").Value,
OvertimeHours = detail.Attribute("OvertimeHours").Value
};
Once you've done this, your dictionary will have a concrete type based on the types of things you specified when you created it:
IDictionary<Tuple<int, int, string>, HoursContainer>
(Note: You could also just use another Tuple<int, int> or whatever here, if you wanted, but the resulting generic type would get unwieldy very fast.)

Categories