I am starting to use Sprache to parse a domain specific language for math expressions. I know I can parse an identifier using something like this:
static readonly Parser<string> Identifier =
from leading in Parse.WhiteSpace.Many()
from first in Parse.Letter.Once()
from rest in Parse.LetterOrDigit.Many()
from trailing in Parse.WhiteSpace.Many()
select new string(first.Concat(rest).ToArray());
From this I want to build a parser that only succeeds if the Identifier token is one of the text values of an Enum. So say I have an Enum called Dimension, with values Dimension.Location and Dimension.Time. I want to make
static readonly Parser<Dimension> DimensionIdentifier = ...
that only succeeds if what is being parsed is an Identifier and if the token string of the identifier is one of the enum names ("Location" or "Time"), and that returns the enum value, Dimension.Location or Dimension.Time respectively. Can someone help with what is probably a simple question? Thanks!
I use the following approach:
public static Parser<TEnum> ParseEnum()
{
return Enum.GetValues(typeof(TEnum))
.Cast<TEnum>()
.Select(value => Parse.IgnoreCase(Enum.GetName(typeof(TEnum), value)).Return(value))
.Aggregate((x, y) => x.Or(y));
}
It's similar to dbugger's answer, as it's still based on Parse.Or, but written in a more functional style.
Very nice solution stolen from here...
http://www.codewise-llc.com/blog/2015/8/13/parsing-enum-values-with-sprache
Build a typed helper class to build the parser for a given enum...
public static class EnumParser<T>
{
public static Parser<T> Create()
{
var names = Enum.GetNames(typeof(T));
var parser = Parse.IgnoreCase(names.First()).Token()
.Return((T)Enum.Parse(typeof(T), names.First()));
foreach (var name in names.Skip(1))
{
parser = parser.Or(Parse.IgnoreCase(name).Token().Return((T)Enum.Parse(typeof(T), name)));
}
return parser;
}
}
Then your parser is simply this...
public static Parser<Dimension> Dimension = EnumParser<Dimension>.Create();
And some unit tests (change the class name to whatever you are using, I was using the Sprache tutorial to get started)...
[Test]
[TestCase("Time", Dimension.Time)]
[TestCase("Location", Dimension.Location)]
public void ShouldGetProperEnumValue(string enumValueName, Dimension expected)
{
var eValue = QuestionnaireGrammar.Dimension.Parse(enumValueName);
Assert.AreEqual(expected, eValue);
}
[Test]
[ExpectedException]
[TestCase("Fredo")]
public void ShouldFailIfNotInList(string enumValueName)
{
var eValue = QuestionnaireGrammar.Dimension.Parse(enumValueName);
}
Interesting library, happy to learn about it.
OK, fairly easy to chain parsers...
Created a copy of your identity parser, and called it Identifier2 to keepit clear...
public static readonly Parser<string> Identifier2 =
from leading in Parse.WhiteSpace.Many()
from first in Parse.Letter.Once()
from rest in Parse.LetterOrDigit.Many()
from trailing in Parse.WhiteSpace.Many()
select new string(first.Concat(rest).ToArray());
Then added a compound parser that takes the results of the Identifier2 parser and uses the Dimension parser...
public static readonly Parser<Dimension> IdentityDimension =
from result in Identifier2
select Dimension.Parse(result);
Though not sure what you are buying -- enum parser already seems to do everything the identifier parser does.
Related
I understand in C# there is such thing as named parameters so that is a bit misleading. The question I have is what they should be called in general.
In my time using libraries in other languages I've sometimes run across predefined values that can be used within functions of a class.
object.myFunc(SPECIAL_VALUE);
It's usually named in all capitol letters and is a very specific value within the function. I figure it behaves much like a public static string but when I use this idea intellisense doesn't pick this up. Can someone inform me of the general name of this kind of parameter option and if they exist, pros and cons of using such a thing.
Thanks,
Normally, those kind of constants in C# are declared and cased like this...
public const String MyConstantThingHere = "Some value";
In Java, it would be more like...
public static final String MY_CONSTANT_THING_HERE = "Some value";
I would simply call these public class constants. See HERE.
Also, for reference: http://www.dofactory.com/reference/csharp-coding-standards
You can also use static imports to refer to them...
See here: static imports in c#
I'm not really sure, what you mean. The concept of named parameters in C# is shown in this example:
namespace Testconsole {
public class obj {
public int myFunc(int value1, int value2 = 4, int value3 = 8) {
return value1 + value2 + value3;
}
}
class Program {
static void Main(string[] args) {
obj adder = new obj();
Console.WriteLine(adder.myFunc(value1: 1, value3: 1));
Console.ReadLine();
}
}
}
Testconsole returns 6: value1 is set with '1', value3 is set with '1', value2 is missing and therefore filled with default value '4'.
Also it is possible to pass a public static String into a Function:
namespace Testconsole {
public class obj {
public String myFunc(String mySpecialValue) {
return "now I " + mySpecialValue;
}
}
class Program {
public static String SPECIAL_VALUE = "got it";
static void Main(string[] args) {
obj Learn = new obj();
Console.WriteLine(Learn.myFunc(SPECIAL_VALUE));
Console.ReadLine();
}
}
}
Console output:
Now I got it.
What you are referring to is a "constant." They serve two purposes:
They ensure that a value that has some meaning (a "special value", as you said) is declared in one place and re-used so that if it changes we change it in one place.
They make our code more readable. For example, we might want two always display the top four items from a list on a web page. And we might have an input that should always be four characters in length. If other people just read our code and see the number 4 here and there it might not be apparent what that number 4 means. We want our code to be as easy to understand as possible.
So when we declare
const int NUMBER_OF_ITEMS_TO_DISPLAY = 4
or
const string MAX_INPUT_LENGTH_SETTINGS_KEY = "maximumInputLength"
Then when we use those constants in our code other people can tell what they mean.
The opposite of using constants is called using "magic numbers" or "magic strings." They just appear in our code without context or explanation. It might just be value we arbitrarily picked, but declaring it as a constant gives it context and meaning.
The capital letters aren't required. They're just a convention that makes constants easy to recognize.
I'm trying to use NCalc to parse some formula from JSON files.
However sometimes I'm needing to reference another object via GUID and get that objects formula:
public class FooObject
{
NCalc.Expression expression;
double Value;
public void SetValue()
{Value = (double)expression.Evaluate()} //this throws "Value was either too large or too small for a Double."
public FooObject()
{ expression = new NCalc.Expression("TechData(b8ef73c7-2ef0-445e-8461-1e0508958a0e)")}
expression.EvaluateFunction += NCalcFunctions;
}
}
public static void NCalcFunctions(string name, FunctionArgs args)
{
if (name == "TechData") //breakpoint shows we never get this far
{
Guid techGuid = new Guid(args.Parameters[0].ToString());
TechSD techSD = _staticData.Techs[techGuid]; //this returns an TechSD object that matches the given guid.
args.Result = techSD.DataExpression;//returns an NCalc expression from the techSD
}
}
SetValue() throws an exception (Value was either too large or too small for a Double), and the custom function never gets called.
I'm suspecting it's trying to parse the GUID. Would this be correct? Is what I'm trying to do possible with NCalc? is there a better tool?
In this NCalc expression:
TechData(b8ef73c7-2ef0-445e-8461-1e0508958a0e)
GUID b8ef73c7-2ef0-445e-8461-1e0508958a0e is not parsed as a string (note there aren't single quotes), NCalc will then try to parse that expression as
b8ef73c7 variable name, undefined.
- subtraction operator.
2ef0 syntax error, it's not an identifier (it starts with a number) and also it's not a valid number (because of e it'll try to parse it as double in scientific notation but it's not valid).
...
You have to use quotes:
TechData('b8ef73c7-2ef0-445e-8461-1e0508958a0e')
Now NCalc will correctly parse this expression and your handler may simply:
var techGuid = Guid.Parse((string)args.EvaluateParameters()[0]);
I am outputting the value of a boolean in my ASP.NET MVC Framework view, and would like to have a lower case true or false, rather than the default of True or False.
I understand that I could just do this:
#this.Model.MyBool.ToString().ToLower()
Alternatively, I could create an extension method. But this defeats the purpose of:
#this.Model.MyBool
I have read the post Why does Boolean.ToString output "True" and not "true", but most of the answers are pretty old.
Are there any more modern ways to accomplish this?
If you only want this for one bool variable you should use #Mohamed 's method. Else you can create an extension method (as you already said yourself):
public static class Extensions
{
public static string ToLowerString(this bool _bool)
{
return _bool.ToString().ToLower();
}
}
Then to use it:
public static void Main()
{
bool testBoolean = true;
Console.WriteLine(testBoolean.ToLowerString());
}
Why don't you try the following
public string MyStringBool
{
get { return MyBool ? "true" : "false" ; }
}
You can also do this which feels syntactically cleaner than true.ToString().ToLower() (in my opinion):
Json.Encode(true);
However under the hood this has far more overhead than using the .ToString.ToLower() implementation.
Json.Encode(object value) has much more error handling as it needs to account for the possibility of more complex objects being passed as arguments.
I did a little benchmarking on this to see what the difference actually is, and on my toaster of a dev box:
var sw0 = Stopwatch.StartNew();
sw0.Stop();
var sw1 = Stopwatch.StartNew();
var t1 = System.Web.Helpers.Json.Encode(true);
var e1 = sw1.ElapsedMilliseconds; // returns 6-9
var sw2 = Stopwatch.StartNew();
var t2 = true.ToString().ToLower();
var e2 = sw2.ElapsedMilliseconds; // returns 0
So really the impact isn't huge for a one off.
Looks like Microsoft recommends the following (incase you need it for XML too):
public static class BoolExtensions
{
public static string ToLowerString(this bool _bool)
{
return _bool.ToString().ToLowerInvariant();
}
}
[ToString.()] returns the constants "True" or "False". Note that XML is case-sensitive, and that the XML specification recognizes "true" and "false" as the valid set of Boolean values. If the String object returned by the ToString(IFormatProvider) method is to be written to an XML file, its String.ToLowerInvariant method should be called first to convert it to lowercase.
Part of my software is using reflection. The issue I am having is that while I can get the type of the property, I cannot convert the string value using the Type from the PropertyInfo. This is the reason why i am using t in the sample code.
The below code demonstrates the issue with the error message as a code comment. The syntax error is on the t. how can I fix this problem? thanks
class Program
{
static void Main(string[] args)
{
Type t = typeof(Letters);
Letters letter = "A".ToEnum<t>(); //-- Type or namespace expected.
}
}
public enum Letters { A, B, C }
//-- This is a copy of the EmunHelper functions from our tools library.
public static class EnumExt
{
public static T ToEnum<T>(this string #string)
{
int tryInt;
if (Int32.TryParse(#string, out tryInt)) return tryInt.ToEnum<T>();
return (T)Enum.Parse(typeof(T), #string);
}
public static T ToEnum<T>(this int #int)
{
return (T)Enum.ToObject(typeof(T), #int);
}
}
Solution:
The following works because when the value is set using reflection, the actual type of Enum is accepted. Where myObject.Letter = result is not.
Type t = currentProperty.PropertyType;
Enum result = Enum.Parse(t, #string) as Enum;
ReflectionHelper.SetProperty(entity, "LetterPropertyName", result);
Thank you all for your help.
Enum.Parse(t, #string) as Enum;
That accomplishes the same thing as the solution you posted.
To be able to call a generic method, the type must be known at compile time. Your use is invalid syntax.
To be able to call your method, you'll have to use reflection to get a reference to the correct generic function so you may call it.
public static object ToEnum(this string s, Type type)
{
var eeType = typeof(EnumExt);
var method = eeType.GetMethod("ToEnum", new[] { typeof(string) })
.MakeGenericMethod(type);
return method.Invoke(null, new[] { s });
}
Then you could call it:
Letters letter = (Letters)"A".ToEnum(t);
p.s., I strongly urge you to change your variable names to something else. Just because you can name your variables as keywords, doesn't mean that you should.
I'm not an expert with reflection, but this works:
static void Main(string[] args)
{
Type t = typeof(Letters);
MethodInfo info = typeof(EnumExt).GetMethod("ToEnum", new Type[] { typeof(string) });
var method = info.MakeGenericMethod(new Type[] { t });
var result = (Letters)method.Invoke(null, new [] { "A" });
Console.ReadLine();
}
I think your question can be understood in two ways:
i) you want to fix a syntax error
ii) you need a working implementation for the function you provide
For i), I would suggest you to change:
Letters letter = "A".ToEnum<t>()
into
Letters letter = "A".ToEnum<Letters>()
because generics are solved at compile time, hence you cannot use variables as parameters.
For ii), you may change the way you provide the type argument, from "generic" argument to "normal" argument (as proposed by Jeff Mercado). By doing so the prototype of your function becomes:
public static T ToEnum(this string #string, Type t)
I conclude with two remarks:
reflection is VERY slow to my experience. I once wrote an assembly parser making extensive use of reflection on enums for register names. It used to run for minutes (which used to make VS debugger complain about non-responsiveness) when an equivalent version without any reflection used to execute in less than 10 seconds.
as Jeff Mercado pointed out, I cannot see any good reason for you to use the same vocable for type names and variable names
I'm new to C# and so forgive me if I have some of my concepts skewed. I'm working with the Canon EDSDK, and to make life easier I'd like to be able to see error messages as text rather than hex values. The EDSDK.cs file contains a long list of errors such as :
public const uint EDS_ERR_TAKE_PICTURE_CARD_NG = 0x00008D07;
public const uint EDS_ERR_TAKE_PICTURE_CARD_PROTECT_NG = 0x00008D08;
Ideally I'd like to put all of these into some sort of lookup table so I can input a HEX errorcode and return the actual message as a string. For example
Hashtable EDSDKErrorCodes = new Hashtable();
EDSDKErrorCodes.Add("0x00008D01", "EDS_ERR_TAKE_PICTURE_AF_NG");
EDSDKErrorCodes.Add("0x00008D08", "EDS_ERR_TAKE_PICTURE_CARD_PROTECT_NG");
etc etc...
The later in my code I could grab the error message returned from one of the EDSDK methods and display it in a human readable form :
errorcode= EDSDK.EdsInitializeSDK();
MessageBox.Show(Convert.ToString(EDSDKErrorCodes[errorcode]));
My question is .. is this a good way to handle all these error codes or is there a better and more efficient way that people would recommend? If so how I could fill my hash table as a series of constants rather than having to use the Add method?
Another way you could do this is by creating an Enum with a description attribute. See here for the full details. This is summarizing how it would look:
public enum ErrorCodes : uint
{
[Description("Description for EDS_ERR_TAKE_PICTURE_CARD_NG")]
EDS_ERR_TAKE_PICTURE_CARD_NG = 0x00008D07,
[Description("Description for EDS_ERR_TAKE_PICTURE_CARD_PROTECT_NG")]
EDS_ERR_TAKE_PICTURE_CARD_PROTECT_NG = 0x00008D08
}
At the very simplest, if you didnt care about a description, you could convert the enum name to string using Enum.GetName
You could use an enum for this.
public enum ErrorCodes : uint
{
EDS_ERR_TAKE_PICTURE_CARD_NG = 0x00008D07,
EDS_ERR_TAKE_PICTURE_CARD_PROTECT_NG = 0x00008D08
}
Then your usage could be something like the following:
ErrorCodes error = (ErrorCodes)EDSDK.EdsInitializeSDK();
MessageBox.Show(Enum.GetName(typeof(ErrorCodes), error));
I would think making an enum would be superior:
public enum CanonErrorCode
{
SomeErrorDescription = 14,
SomeOtherErrorDescription = 15
// .. etc
}
You simply do the conversion from hex to integer. Then you can simply call it like so:
// Samples
var myErrorCode = (CanonErrorCode)someIntegerValue;
var myErrorCode = CanonErrorCode.SomeOtherErrorDescription;
If you want human formatted error descriptions, then I would suggest some kind of mapping like:
static Dictionary<int, string> errorlookups = new Dictionary<int, string>();
errorLookups.Add(10, "This is the human readable error message.");
errorLookups.Add(17, "Create this guy once in a static constructor.");
You could use a Generic Dictionary instead of a hashtable
Dictionary<uint, String> myDict = new Dictionary<uint, String>();
You can then retrieve the value you want by doing
MessageBox.Show(myDict[errorCode]);
For adding, I think you might be able to do something with reflection to reflect over the EDSDK class and find all the constant uint members. Then iterate through that list adding the value and the constant name.
Ok, another take since we are rolling :-)
This solution also produces a human-readable error string if the current error code is not defined.
Create an Enum like so:
public enum ErrorCode : uint
{
EDS_ERR_TAKE_PICTURE_CARD_NG = 0x00008D07,
EDS_ERR_TAKE_PICTURE_CARD_PROTECT_NG = 0x00008D08
}
Then, create an Extension Method for the enum type like this:
public static class ErrorHandler
{
public static string ToErrorString(this ErrorCode errorCode)
{
return Enum.IsDefined(typeof(ErrorCode), errorCode) ?
errorCode.ToString() : "Undefined error code";
}
}
And finally, use it like that:
var errorCode = (ErrorCode)EDSDK.EdsInitializeSDK();
MessageBox.Show(errorCode.ToErrorString());
First of all, you should use a generic dictionary instead of a hashtable. Second, I don't know anything about the Canon EDSDK, but it seems odd that it returns an error code as a hex string rather than simply a uint as the constants are defined. Is that really how they are being returned? If so, that's weird, but you should be able to convert them to an integer, otherwise, you can skip that step.
If you want to create the array using reflection, you could do something like this:
Dictionary<int,String> EDSDKErrorCodes = new Dictionary<int,String>;
System.Reflection.FieldInfo[] fields = typeof(EDSDK).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
foreach (System.Reflection.FieldInfo field in fields) {
EDSDKErrorCodes[(uint)field.GetValue(null)] = field.Name;
}
And you can then access it using:
MessageBox.Show(EDSDKErrorCodes[errorCode]);
If errorCode is a string, convert it first using:
uint errorNumber = uint.Parse(errorCode.Substring(2), System.Globalization.NumberStyle.HexNumber);