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).
Related
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];
}
}
I have a database which stores user inputs in an abstract stringified form. These user inputs have a varchar column which describes its type (string, decimal, bool, dropdown etc).
Now this get's send to the front end to display some input elements in the browser. This works great!
However since the input is so generic the value is also a varchar. The problem I am facing is that I need to do some validation on the value. (e.g. some string input have a maxLength or regex specified, a decimal can have a min and max value).
so once I get back the value the user entered it is in string format and I want to parse it to the correct native type so I can start validating it.
I would like a function which returns the parsed value in it's correct type.
so I would have a function which is something like this:
public {something here} ParseValue(InputObject object, string type) {
// parse here based on type
// InputObject has a few properties like value, min, max, regex etc
// all as a string.
// for instance if type is datetime I want to return a new object
// which has parsed the value, min and max as datetime.
// it should also be possible for the type to be decimal and min, max
// and value should be decimal in the outputObject
}
I am coming from a dynamically typed background so I have no idea how to do something like this. or even if it is possible.
any help is appreciated!
You'd be best off if you don't directly try to evaluate the type by the Database-Datatype and instead store the "real" type in a seperate DB-Column. Except if you build an association between C#-Types and Database-Types because you can do something like this then:
String val = "123";
String type = "System.Int32";
Type tempType = Type.GetType(type);
if (tempType == null)
return null;
dynamic result = Convert.ChangeType(val, tempType);
Of course this would be applicable to the boundary values also. Note that Convert.ChangeType only works for very popular Types and is not universally useable and that it throws an Exception if theres something failing which need to be catched also.
What you could do is create an interface IValidatable that defines a method like Validate(). Then you could use that as a return type. Then you just parse your value using a switch (probably delegate this to some method or class) to an implementation of IValidatable. E.g.
public interface IValidatable {
bool Validate();
}
public class ValidateableInteger : IValidatable {
private int _value;
public ValidateableInteger(int i) {
_value = i;
}
bool Validate() {
//code where you validate your integer.
}
}
Note that this is not very flexible as you only have 1 method called validate, though clearly you can define multiple more generic methods that could implement different validations.
Moreover you can create more specific interfaces for e.g. numeric types (e.g. IValidateableNumeric and ValidateableInt : IValidateableNumeric)
Note that you're basically typing your input here though, which is kindof weird and unnecessary given the fact that you can just work with typed data to begin with.
In the end I would discourage people from bypassing type system this way. In this case especially there are plenty better ways of creating form elements while using typed data (checkout the Razor template engine).
I've got a enum type defined in my C# code that corresponds to all possible values for the NetConnectionStatus field in Win32_NetworkAdapter WMI table, as documented here.
The documentation shows that the integers 0 through 12 each have a unique status name, but then all integers between 13 and 65,535 are lumped into one bucket called "Other." So here's my code:
[Serializable]
public enum NetConnectionStatus
{
Disconnected = 0,
Connecting = 1,
Connected = 2,
Disconnecting = 3,
HardwareNotPresent = 4,
HardwareDisabled = 5,
HardwareMalfunction = 6,
MediaDisconnected = 7,
Authenticating = 8,
AuthenticationSucceeded = 9,
AuthenticationFailed = 10,
InvalidAddress = 11,
CredentialsRequired = 12,
Other
}
This works fine for the values that are not Other. For instance, I can do this:
var result = (NetConnectionStatus) 2;
Assert.AreEqual(NetConnectionStatus.Connected, result);
But for anything in that higher numeric range, it doesn't work so great. I would like it if I could do this:
var result = (NetConnectionStatus) 20;
Assert.AreEqual(NetConnectionStatus.Other, result);
But right now that result variable gets assigned the literal value 20 instead of Other. Is there some out-of-the-box way of accomplishing this, something akin to Parse() but for integers instead of strings, or perhaps some special attribute I'm unaware of? I would prefer to not write my own wrapper method for this if there is already a good way to accomplish this.
If you have a string value, then the closest thing I can think of is to use Enum.TryParse:
NetConnectionStatus result;
if (Enum.TryParse(stringValue, out result) == false)
result = NetConnectionStatus.Other;
For an integer value that you're casting, you can use:
result = (NetConnectionStatus)integerValue;
if (Enum.GetValues(typeof(NetConnectionStatus)).Contains(result) == false)
result = NetConnectionStatus.Other;
Not really ideal, but in C# enums aren't much more than fancy names for integral values, so it's valid to stuff an integer value not in the defined values of the enums into a value of that enum type.
This solution will handle negative numbers, or cases where you have gaps in your enum values more elegantly than doing numerical comparisons.
it would be nice but no. How about
var result = (NetConnectionStatus) 20;
Assert.IsTrue(result >= (int)NetConnectionStatus.Other);
.NET does not such thing as a "any other" enumeration value bucket. Technically, enumeration (enum) is a pretty set of named constants of some underlying type (which is one of following: sbyte, short, int, long and their unsigned counterparts). You can cast an enum value to/from a corresponding type without any losses, as in this example:
enum TestEnum:int // Explicitly stating a type.
{
OnlyElement=0
}
class Program
{
static void Main(string[] args)
{
// Console.WriteLine implicitly calls ToString of the TestEnum.OnlyElement.
Console.WriteLine("OnlyElement == {0}", TestEnum.OnlyElement);
//TestEnum.OnlyElement equals to 0, as demonstrated by this casting:
Console.WriteLine("(int)OnlyElement == {0}", (int)TestEnum.OnlyElement);
//We can do it in reverse...
Console.WriteLine("(TestEnum)0 == ",(TestEnum)0);
// But what happens when we try to cast a value, which is not
// representable by any of enum's named constants,
// into value of enum in question? No exception is thrown
// whatsoever: enum variable simply holds that value, and,
// having no named constant to associate it with, simply returns
// that value when attempting to "ToString"ify it:
Console.WriteLine("(TestEnum)5 == {0}", (TestEnum)5); //prints "(TestEnum)5 == 5".
Console.ReadKey();
}
}
I'd like to repeat it again, enum in .NET is simply a value of the underlying type with some nice decorations like overriden ToString method and flags checking (look here or here if you want to know more about flags). You cannot have an integer with only 14 values like "0..12 and everything else", and so you cannot have such enum. In your example, NetConnectionStatus.Other simply receives single literal value (I assume it would most probably be '13', as the next available positive value of underlying type - however it actually depends on the compiler) as any other enumeration constant would do if not specified explicitly - and, obviously, it does not become a bucket.
However, there are options to achieve simple equation checks for integers/bytes/shorts/longs - and enums alike. Consider this extension method:
static bool IsOther(this NetConnectionStatus A)
{
return (A < (NetConnectionStatus)0) || (A > (NetConnectionStatus)12);
}
Now you can have a simple assertion like this:
var result = (NetConnectionStatus)10;
Trace.Assert(result.IsOther()); //No assertion is triggered; result is NetConnectionStatus.AuthenticationFailed
and
var result = (NetConnectionStatus)20;
Trace.Assert(result.IsOther()); //Assertion failed; result is undefined!
(Of course you can replace IsOther method with IsNotOther, overload it and pretty much anything else you could do with a method.)
Now there is one more thing. Enum class itself contains a method called IsDefined, which allows you to avoid checks for specific enum's value boundaries (<0, >12), therefore preventing unwanted bugs in case enum values would ever be added/removed, at the small performance cost of unboxing and checking each value in enum for a match (I'm not sure how this works under the hood though, I hope these checks are optimized). So your method would look like this:
static bool IsOther(NetConnectionStatus A)
{
return !Enum.IsDefined(typeof(NetConnectionStatus), A);
}
(However, concluding from enum's name, it seems like you want to make a network application/server, and for these performance might be of very great importance - but most probably I'm just being paranoid and this will not be your application's bottleneck. Stability is much more of concern, and, unless you experience real troubles with performance, it is considered to be much better practice to enable as much stability&safety&portability as possible. Enum.IsDefined is much more understandable, portable and stable than the explicit boundaries checking.)
Hope that helps!
Thanks everyone for the replies. As confirmed by all of you, there is indeed no way to do this out-of-the-box. For the benefit of others I thought I'd post the (custom) code I ended up writing. I wrote an extension method that utilizes a custom attribute on the enum value that I called [CatchAll].
public class CatchAll : Attribute { }
public static class EnumExtensions
{
public static T ToEnum<T, U>(this U value) where T : struct, IConvertible where U : struct, IComparable, IConvertible, IFormattable, IComparable<U>, IEquatable<U>
{
var result = (T)Enum.ToObject(typeof(T), value);
var values = Enum.GetValues(typeof(T)).Cast<T>().ToList();
if (!values.Contains(result))
{
foreach (var enumVal in from enumVal in values
let info = typeof(T).GetField(enumVal.ToString())
let attrs = info.GetCustomAttributes(typeof(CatchAll), false)
where attrs.Length == 1
select enumVal)
{
result = enumVal;
break;
}
}
return result;
}
}
So then I just have to apply that [CatchAll] attribute to the Other value in the enum definition. Then I can do things like this:
int value = 13;
var result = value.ToEnum<NetConnectionStatus, int>();
Assert.AreEqual(NetConnectionStatus.Other, result);
And this:
ushort value = 20;
result = value.ToEnum<NetConnectionStatus, ushort>();
Assert.AreEqual(NetConnectionStatus.Other, result);
I know how to pass a list of values in arrays
int[] unit = new int[] {1,-3,3,4};
string[] letter_grade = new string[] {"a+","B","c","W"};
double totalGPA;
GPA get = new GPA();
get.getgpa(unit, letter_grade ,out totalGPA);
but i wanted to know how to pass it using structs i can send single value but not getting how to send list of values
double totalGPA;
GPA get = new GPA();
GPAList[] Val = new GPAList[1];
Val[0].grade ="d";
Val[0].unitgrade = 4;
get.getgpa(Val[0], out totalGPA);
You just pass the struct array Val to getgpa:
get.getgpa(Val, out totalGPA);
Then getgpa should accept a array of GPA structs:
void getgpa(GPA[] gpas, out double totalGPA)
As far as I've understood your question, you want to pass a collection of values using struct.
A struct is not meant for storing collections, for collections we've arrays, arraylist, hashtable etc in c#.
The purpose of a struct is to represent a complete information into a single datatype. Structures are simply lightweight classes, they are no way used for collections.
If you want to use structures for storing collection of values, then again you'll have to make array of structures.
If you want to pass array of a structure, then in the formal parameter list, you should expect array of that structure itself, for eg.
**get.getgpa(Val, out totalGPA);**
for above code to work, the signature of getgpa function should be:
**getgpa(GPA[] arr,out double totalGPA)**
Hope this helps.
I have a scenario where I'm using a Dictionary to hold a list of transaction types that a certain system accepts. The key in the Dictionary is an enum field, the value is an int.
At some point in the system, we're going to want to do something like this:
sqlCommand.Parameters.AddWithValue("#param", LookupDictionary[argument.enumField]);
When we look up the field in the dictionary, we're going to get the correct integer value to feed to the database. I've thought about actually using the enum int value for this, but that's not exactly right. We're interacting with a system where we need to feed a magic number in to represent the kind of update we're doing.
The code above works just fine. I have an initializer method that adds the known types:
LookupDictionary = new Dictionary<mynamespace.myproject.myclass.enumType, int>();
LookupDictionary.Add(enumType.entry1, 4);
LookupDictionary.Add(enumType.entry2, 5);
LookupDictionary.Add(enumType.entry3, 6);
This code also works fine.
But up above, before I actually get in to using the LookupDictionary, I validate that the request being made is actually set to an enum value we support. That's LookupDictionary's main reason to be, it holds the valid ones (there are valid enum entries that this method doesn't work with).
This is the code that doesn't work: the system fails to recognize that the enums match. In the debugger, I can see that the entries list in LookupDictionary does show that it has the value for entry2 - it just calls it like that, entry2. The incoming enumField on the other hand has the full namespace; mynamespace.myproject.myclass.enumType.entry2 - I imagine this is why it doesn't see them as being the same.
if (!LookupDictionary.ContainsKey(argument.enumField))
{
throw new InvalidOperationException("argument.enumField not valid in blahMethod.");
}
Did I mention that this is being passed across a WCF service? But I'm not using an auto-generated proxy ... both projects on both sides of the wire share the types as a project reference, and I build up my channel client in code.
Any ideas? Am I doing it wrong? Do Dictionaries with Enums as keys not work well? Is it a WCF thing?
Note: thanks for the suggestions regarding setting the enums up to contain the magic int. I wanted to set those in a configuration, however, as its possible that the "magic numbers" 4 5 and 6 might change down the road. So if I code them in to the enum as suggested:
public enum MyEnum
{
MyValue1 = 4,
MyValue2 = 5,
MyValue3 = 6
}
I lose the ability to write a method that sets up the magic numbers in the future at run time; instead it would require a code change.
Instead of using the enum as the key, use the integer representation of the enum.
For instance:
LookupDictionary = new Dictionary<int, int>();
LookupDictionary.Add((int)enumType.entry1, 4);
LookupDictionary.Add((int)enumType.entry2, 5);
LookupDictionary.Add((int)enumType.entry3, 6);
That way, you can use the same 'ContainsKey' method of the dictionary. I'm not sure this is much better performance than a List<int>
You shouldn't need a lookup table here at all:
public enum MyEnum
{
MyValue1 = 4,
MyValue2 = 5,
MyValue3 = 6
}
// Sample usage
MyEnum firstEnum = MyEnum.MyValue1;
int intVal = (int)firstEnum; // results in 4
// Enum Validation
bool valid = Enum.IsDefined(typeof(MyEnum), intVal); // results in true
Can you considered typing your enumeration explicitly as int (or whatever the underlying type is) and then setting the value of each of your enumerations to the database value? You've already tightly coupled the enumeration to the database, so either the relationship will be dictated in C# (current hard-coding) or by SQL (perhaps a proc that returns the ID as well as a string that can be parsed into an enumeration.)
Using the assumption that your enumeration is an int...
enum enumType {
entry1 = 4,
entry2 = 5,
entry3 = 6
}
When adding your parameter you would then just cast as the enum's underlying type.
sqlCommand.Parameters.AddWithValue("#param", (int)argument.enumField);
You can explicitly set the values of the enum using the syntax
enum ArgumentTypes {
Arg1 = 1;
Arg2 = 3;
Arg3 = 5;
}
You don't need to keep each value sequential in the enum for this syntax to work.
To validate that only parameters that are valid for the method are ever used, try this sample code. Note I suggest using an ArgumentException over an InvalidOperationException in this context.
public void DoDbWork(ArgumentTypes argType, object otherParameter)
{
if (argType == ArgumentTypes.Arg3) {
throw new ArgumentException("Argument of value " + argType + " is not valid in this context", "argType");
}
// Handle db transaction here
}
To add the int value as the parameter:
cmd.Parameters.AddWithValue("#paramName", (int)argType);