How can I avoid entering strings for known keys in C# - c#

I have the following code:
switch(first)
{
case 'A':
vm.Content = contentService.Get("0001000", vm.RowKey);
return View("Article", vm);
case 'F':
vm.Content = contentService.Get("0002000", vm.RowKey);
return View("FavoritesList", vm);
}
'A' refers to a page type of Article with a key of "0001000"
'F' refers to a page type of Favorite with a key of "0002000"
Is there a way in C# that I could avoid having to code in the keys as a string?
Some way that would allow me to code in by the key abbreviation or name
and then have C# convert this to a string?
Can I use Enum for this? This seems ideal but I am not sure how to set up an Enum.

You may think about dictionary (if I right understood your question)
//class for holding relation between the code and page name
public class Data
{
public string Code {get;set;}
public string PageName {get;set;}
}
var dic = new Dictionary<string, Data >{
{"A", new Data{Code="0001000", PageName = "Article"},
{"F", newe Data{Code="0002000", PageName="FavoritesList"}
}
and after use it like:
Data dt = null;
if(dic.TryGetValue(first, out dt)) { // *first* is parameter you use in switch
vm.Content = contentService.Get(dt.Code, vm.RowKey);
return View(dt.PageName, vm);
}

You can use enums and use extension methods to allow an alternative text output.
The enum:
public enum PageTypes
{
A,
F
}
The extension method (needs to be in the top level of your project):
public static class Extensions
{
public static string getText(this PageTypes type)
{
switch (type)
{
case PageTypes.A:
return "0001000";
case PageTypes.F:
return "0002000";
default:
return null;
}
}
}
And your code:
PageTypes type;
//assing value to type:
//type = ...
var vm.Content = contentService.Get(type.getText(), vm.RowKey);
switch (type)
{
case PageTypes.A:
return View("Article", vm);
case PageTypes.F:
return View("FavoritesList", vm);
}
Now you do not need to use strings to retrieve the values.

I would put the keys in a dictionary, e.g.
var keyData = new Dictionary(char,string);
keyData.Add('A',"0001000");
keyData.Add('A',"0001000");
keyData.Add('F',"0002000");
You could then reference them using
var value = keyData['A'];

Related

Can I use reflection to shorten this code?

I have some code that I feel like I should be able to shorten incredibly, but I can't figure out how to do it.
I have a base class called Message and may classes that derive from it.
namespace ModalVR {
public class Message {
public string message;
public Message() {
this.message = this.ToString();
}
}
}
The subclasses get converted to JSON, and I have a function that receives this JSON and I need to create the appropriate class. However the function that does it has a huge case statement and I feel that there must be a better way of doing this. This is what that function looks like.
public Message ConstructMessageFromJSON(string JSON) {
string messageName = JsonUtility.FromJson<Message>(JSON).message;
Message derivedMessage = null;
switch(messageName) {
case "ModalVR.GetBatteryInfo": {
derivedMessage = JsonUtility.FromJson<GetBatteryInfo>(JSON);
break;
}
case "ModalVR.GetBatteryInfoResponse": {
derivedMessage = JsonUtility.FromJson<GetBatteryInfoResponse>(JSON);
break;
}
// Many more case statements snipped out
default: {
LogManager.Log("Received unknown message of " + messageName, LogManager.LogLevel.Error);
break;
}
}
return derivedMessage;
}
Is there any way I can replace this huge case statement with something simpler?
Thanks in advance
John Lawrie
Using reflection only, you can do:
string messageName = "ModalVR.GetBatteryInfo";
Type messageType = Assembly.GetAssembly(typeof(Message)).GetType(messageName);
Message derivedMessage = (Message)JsonUtility.FromJson(json, messageType);
It retrieves the Assembly in which you have defined your Message class and then search for the requested type in this assembly.
The easiest way would be to create a dictionary like that:
var typeMatches = new Dictionary<string, Type>
{
{"ModalVR.GetBatteryInfo", typeof(GetBatteryInfo)}
};
and then just get the value from it: (that's C# 7)
if (!typeMatches.TryGetValue(messageName, out var messageType))
{
LogManager.Log("Received unknown message of " + messageName, LogManager.LogLevel.Error);
return;
}
var derivedMessage = (Message) JsonUtility.FromJson(JSON, messageType);

Enum with multiple descriptions

I have my enum like this.
public enum Gender
{
Man = 1,
Woman = 2
}
And I use ASP MVC4 to display the choices in a drop down like this.
#Html.DropDownListFor(model => model.Gender, new SelectList(Enum.GetValues(typeof(Namespace.Models.Enum.Gender))))
This works like a charm, it display Man/Woman in the drop down.
My problem is that I would like to show different names on those enums in different contexts.
Like one context would be if you are a Mom or a Dad. I would like to use the gender enum as base, but display Mom/Dad instad of Man/Woman.
Another context would be Boy/Girl, I still would like to use the gender enum, but display a different text.
Is this possible in any way?
EDIT
I used Kevin's solution and also added another extention method like this.
public static List<KeyValuePair<string, int>> GetValues(IGenderStrategy genderStrategy)
{
Dictionary<string, int> arr = new Dictionary<string, int>();
foreach (Gender g in System.Enum.GetValues(typeof(Gender)))
arr.Add(g.ToValue(genderStrategy), (int)g);
return arr.ToList();
}
Which I used like this in my view.
#Html.DropDownListFor(model => model.Gender, new SelectList(Chores.Models.Enum.EnumExtentions.GetValues(new Chores.Models.Enum.ParentStrategy()), "value", "key"))
I like #RakotVT answer of using an extension method but would extend it a bit further as you would need a new extension method for every situation which is not great.
I think a variation of the Strategy pattern might work better here (http://www.dofactory.com/Patterns/PatternStrategy.aspx)
Something like this -
public enum Gender
{
Man = 1,
Woman = 2
}
public interface IGenderStrategy
{
string DisplayName(Gender gender);
}
public class ParentStrategy : IGenderStrategy
{
public string DisplayName(Gender gender)
{
string retVal = String.Empty;
switch (gender)
{
case Gender.Man:
retVal = "Dad";
break;
case Gender.Woman:
retVal = "Mom";
break;
default:
throw new Exception("Gender not found");
}
return retVal;
}
}
public static class EnumExtentions
{
public static string ToValue(this Gender e, IGenderStrategy genderStategy)
{
return genderStategy.DisplayName(e);
}
}
public class Test
{
public Test()
{
Gender.Man.ToValue(new ParentStrategy());
}
}
Try to add Extentions class for your Enum. Here is an example of this class.
public static class EnumExtentions
{
public static string ToChildValue(this Gender e)
{
string retVal = string.Empty;
switch (e)
{
case Gender.Man:
retVal = "Boy";
break;
case Gender.Woman:
retVal = "Girl";
break;
}
return retVal;
}
public static string ToParentValue(this Gender e)
{
string retVal = string.Empty;
switch (e)
{
case Gender.Man:
retVal = "Dad";
break;
case Gender.Woman:
retVal = "Mom";
break;
}
return retVal;
}
}
Dunno if this is the neatest way, but how about something like:
#functions{
IEnumerable<SelectListItem> GetGenderSelectList(GenderContext genderContext)
{
return Enum.GetValues(typeof(Namespace.Models.Enum.Gender)).ToList().ConvertAll(x => new SelectListItem(){Value= x.ToString(), Text= GetGenderDescription(x, genderContext)});
}
string GetGenderDescription(Gender gender, GenderContext genderContext)
{
switch (GenderContext)
{
case Children: return gender == Man? "Boy" : "Girl";
case Parents: return gender == Man? "Dad" : "Mom";
default: return gender.ToString();
}
}
}
#Html.DropDownListFor(model => model.Gender, GetGenderSelectList(model.GenderContext))
Here 'GenderContext' is another Enum.
obviously you don't need to have those functions in the page functions - Could just add the list of items to the ViewBag before even getting to the view.

C# get/set solution

To give some background I'm trying to solve the Project Euler Problem 54 involving poker hands. Though there's infinite approaches to this. What I would like to do is enumerate through a list of strings, for example:
{ "8C", "TS", "KC", "9H", "4S" };
I would like to "get" an instance of class card with properties value, and suit, for each respective string. I've not yet utilized get/set so maybe there is an obvious approach to this I'm missing.
Ultimately I would like to have a list of objects type Card, I don't mind building all the card's ahead of time, such that "2H" returns an instance of type Card where suit = Hearts, and value = 2, for example.
I know this code is wrong, but it should give an idea of what I'm trying to do. Any suggestions would be appreciated.
class Card
{
public string suit;
public int value;
public string cardname
{
get
{
if (cardname == "2H") Card TwoH = new Card();
TwoH.suit = "Hearts"
TwoH.value = 2;
return TwoH;
}
}
}
Why not make a constructor that fills suit and value based on a string parameter
public Card(string name)
{
switch(name)
{
case "2H":
this.suit = "Hearts";
this.value = 2;
break;
//...
}
}
This might not be the exact solution you seem to be asking for but if the values you'll be getting (eg 2H, 3C etc) are all 2 characters long, then you can try this:
public class Card
{
public string suit { get; set; }
public int value { get; set; }
public static Card GetCard(string cardName)
{
string tmpSuit;
int tmpValue;
char[] cardNameParts = cardName.ToCharArray();
switch(charNameParts[0])
{
case "A":
tmpValue = 1;
break;
case "2":
tmpValue = 2;
break;
...
}
switch(charNameParts[1])
{
case "H":
tmpSuit= "Hearts";
break;
case "C":
tmpSuit= "Clubs";
break;
...
}
return new Card() { suit = tmpSuit, value = tmpValue };
}
}
I would do it like that:
public class Card
{
public string Suit { get; set; }
public int Value { get; set; }
public static Card FromString(string s)
{
if (s == "2H") return new Card() { Suit = "Hearts", Value = 2 };
else if (s == "....")
...
else return null;
}
}
I have converted your suit and value field into properties and instead of some getter method which in your case wouldn't work I have added a static method.
You can use it like this Card card2H = Card.FromString("2H");
Maybe use two switch statements, first
switch (cardname[0])
{
...
}
then
switch (cardname[1])
{
...
}
Before that, check that cardname.Length == 2. In each switch, have a default section where you throw an exception in case the char value doesn't make sense.

Parsing string as enum

Consider the enum below:
public enum TestType
{
Mil,
IEEE
}
How can I parse the folowing strings to the above enum?
Military 888d Test in case of TestType.Mil
Or
IEEE 1394 in case of TestType.IEEE
My idea was to check if the first letters of string matches 'Mil' or 'IEEE' then I would set it as the enum I want, but the problemis there are other cases that should not be parsed!
Already answred by me : How to set string in Enum C#?
Enum cannot be string but you can attach attribute and than you can read the value of enum as below....................
public enum TestType
{
[Description("Military 888d Test")]
Mil,
[Description("IEEE 1394")]
IEEE
}
public static string GetEnumDescription(Enum value)
{
FieldInfo fi = value.GetType().GetField(value.ToString());
DescriptionAttribute[] attributes =
(DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute),
false);
if (attributes != null &&
attributes.Length > 0)
return attributes[0].Description;
else
return value.ToString();
}
here is good article if you want to go through it : Associating Strings with enums in C#
My understanding of your situation is that the string can come in any format. You could have strings like "Military 888d Test", "Mil 1234 Test", "Milit xyz SOmething"...
In such a scenario, a simple Enum.Parse is NOT going to help. You will need to decide for each value of the Enum, which combinations do you want to allow. Consider the code below...
public enum TestType
{
Unknown,
Mil,
IEEE
}
class TestEnumParseRule
{
public string[] AllowedPatterns { get; set; }
public TestType Result { get; set; }
}
private static TestType GetEnumType(string test, List<TestEnumParseRule> rules)
{
var result = TestType.Unknown;
var any = rules.FirstOrDefault((x => x.AllowedPatterns.Any(y => System.Text.RegularExpressions.Regex.IsMatch(test, y))));
if (any != null)
result = any.Result;
return result;
}
var objects = new List<TestEnumParseRule>
{
new TestEnumParseRule() {AllowedPatterns = new[] {"^Military \\d{3}\\w{1} [Test|Test2]+$"}, Result = TestType.Mil},
new TestEnumParseRule() {AllowedPatterns = new[] {"^IEEE \\d{3}\\w{1} [Test|Test2]+$"}, Result = TestType.IEEE}
};
var testString1 = "Military 888d Test";
var testString2 = "Miltiary 833d Spiral";
var result = GetEnumType(testString1, objects);
Console.WriteLine(result); // Mil
result = GetEnumType(testString2, objects);
Console.WriteLine(result); // Unknown
The important thing is populating that rules object with the relevant regular expressions or tests. How you get those values in to the array, really depends on you...
try this var result = Enum.Parse(type, value);
Please try this way
TestType testType;
Enum.TryParse<TestType>("IEEE", out testType);
and it you want to compare string then
bool result = testType.ToString() == "IEEE";
Simple method will do it for you:
public TestType GetTestType(string testTypeName)
{
switch(testTypeName)
{
case "Military 888d Test":
return TestType.Mil;
case "IEEE 1394":
return TestType.IEEE;
default:
throw new ArgumentException("testTypeName");
}
}
EDIT
use Enum.GetNames:
foreach (var value in Enum.GetNames(typeof(TestType)))
{
// compare strings here
if(yourString.Contains(value))
{
// what you want to do
...
}
}
If you using .NET4 or later you can use Enum.TryParse. and Enum.Parse is available for .NET2 and later.

Restrict a string property to a pre-defined set of values (workaround for no hyphens in an enum)

I have a string property that I would like to be able to force two things with:
- It can only be set to specific vaues within a pre-defined list,
- Error checking of the property's value can be performed at compile time.
An enum fits the bill perfectly except that in my list of pre-defined strings there is one with a hyphen and enum values cannot contain hyphens. To illustrate the ideal solution if an enum could contain hyphens I would create an enum of:
public enum SIPEventPackagesEnum
{
dialog,
message-summary,
refer
}
To use:
SIPEventPackagesEnum EventPackage = SIPEventPackagesEnum.message-summary;
To set:
string eventPackageStr = "message-summary";
SIPEventPackagesEnum EventPackage = (SIPEventPackagesEnum)Enum.Parse(typeof(SIPEventPackagesEnum), eventPackageStr, true);
In the above cases it's impossible to set the EventPackage property to anything but one of the enum values and is intuitive to use since intellisense will list the available options.
Due to the inability to use a hyphen, and I cannot change the pre-defined list to remove the hyphen, the very crude approach is to use a struct and have a "Value" property on the struct that does the enforcing in its setter, see below. It's very verbose compared to using an enum and also doesn't allow any compile time checking and isn't very intuitive.
Has anyone encountered this problem before and have a better solution? I have multiple lists with items containing hyphens so it's not a once off.
public struct SIPEventPackages
{
public const string DIALOG = "dialog";
public const string MESSAGE_SUMMARY = "message-summary";
public const string REFER = "refer";
public string Value
{
get { return Value; }
set
{
if (IsValid(value))
{
Value = value.ToLower();
}
else
{
throw new ArgumentException(value + " is invalid for a SIP event package.");
}
}
}
public bool IsValid(string value)
{
if (value.IsNullOrBlank())
{
return false;
}
else if (value.ToLower() == DIALOG || value.ToLower() == MESSAGE_SUMMARY || value.ToLower() == REFER)
{
return true;
}
else
{
return false;
}
}
public override string ToString()
{
return Value;
}
}
Seems this is simple using an delegate
Func<string, bool> isValid = str =>
{
List<string> validLst = new List<string>() { "dialog","message-summary","refer" };
if (validLst.Find(x => string.Equals(x,str,StringComparison.InvariantCultureIgnoreCase)) == null)
return false;
return true;
};
var teststr1 = "message-summary";
var teststr2 = "wrongone";
isValid(teststr1);
isValid(teststr2);
Uptdate:
Otherwise you can use the enum approach in a little different way. Have a enum value without an hyphen. And just strip the hyphens from your source string when parse the enum values. this will work as you expected
public enum SIPEventPackagesEnum
{
dialog,
messagesummary,
refer
}
string eventPackageStr = "message-summary";
SIPEventPackagesEnum EventPackage = (SIPEventPackagesEnum)Enum.Parse(typeof(SIPEventPackagesEnum), eventPackageStr.Replace("-",""), true);
You could create the enum without -, then have a static Dictionary<SIPEventPackagesEnum
,string> in a helper class mapping from enum value to string to convert from enum to string, then use Enum.Parse(typeof(SIPEventPackagesEnum), str.Replace("-", "")) when converting from string to enum.
Or use _ instead of - and replace _ with - and vice versa when required
Or use camel case in the enum values and replace a capital letter within the enum name with "-<lowercase letter>" using a regex
I managed to fine tune my approach so that it's almost as good as an enum albeit with a lot more plumbing code required. It's worth the plumbing code to save potential misues problems in the future.
public struct SIPEventPackage
{
public static SIPEventPackage None = new SIPEventPackage(null);
public static SIPEventPackage Dialog = new SIPEventPackage("dialog");
public static SIPEventPackage MessageSummary = new SIPEventPackage("message-summary");
public static SIPEventPackage Refer = new SIPEventPackage("refer");
private string m_value;
private SIPEventPackage(string value)
{
m_value = value;
}
public override string ToString()
{
return m_value;
}
public static SIPEventPackage Parse(string value)
{
if (!IsValid(value))
{
throw new ArgumentException("The value is not valid for a SIPEventPackage.");
}
else
{
string trimmedValue = value.Trim().ToLower();
switch (trimmedValue)
{
case "dialog": return SIPEventPackage.Dialog;
case "message-summary": return SIPEventPackage.MessageSummary;
case "refer": return SIPEventPackage.Refer;
default: throw new ArgumentException("The value is not valid for a SIPEventPackage.");
}
}
}
}
There's a little bit more plumbing required, implementing an IsValid method and operator == and a few more, but the main thing is I can now use the struct in almost an identical way to an enum and can have items with hyphens.

Categories

Resources