Using strings instead of enums? - c#

Is it common place to use a string for comparison as opposed to an enum?

I am aware about your context, but as a first step you can just refactor this way:
Step 1
if (typeOfObject == "UAV")
{
DoSomeWork(_stkObjectRootToIsolateForUavs);
}
else if (typeOfObject == "Entity")
{
DoSomeWork(_stkObjectRootToIsolateForEntities);
}
private void DoSomeWork(IAgStkObject agStkObject)
{
IAgStkObject stkObject = agStkObject.CurrentScenario.Children[stkObjectName];
IAgDataProviderGroup group = (IAgDataProviderGroup)stkUavObject.DataProviders["Heading"];
IAgDataProvider provider = (IAgDataProvider)group.Group["Fixed"];
IAgDrResult result = ((IAgDataPrvTimeVar)provider).ExecSingle(_stkObjectRootToIsolateForUavs.CurrentTime);
stkObjectHeadingAndVelocity[0] = (double)result.DataSets[1].GetValues().GetValue(0);
stkObjectHeadingAndVelocity[1] = (double)result.DataSets[4].GetValues().GetValue(0);
}
Then consider replasing if's with switch:
Step 2
switch (typeOfObject)
{
case "UAV":
DoSomeWork(_stkObjectRootToIsolateForUavs);
break;
case "Entity":
DoSomeWork(_stkObjectRootToIsolateForEntities);
break;
default:
throw new NotImplementedException():
}
This can be even better when using enums.

At the very least, the strings should be declared as constants (or perhaps readonly fields) somewhere, instead of spread out through the code. However, this looks like the schoolbook example for when to use an enum.
public enum ObjectType
{
UAV,
Entity,
// and so on
}

To add to #Restuta's answer, I'd use a
IDictionary<MyEnumifiedString, Action<IAgStkObject>>
to get rid of that if.

I'd agree with #Frederik that this seems a perfect case for using enums, but it could be that the only thing you can get out of the application is a string. In which case your example is perfectly OK.
Oh yes - and make sure you have the string constants defined in one place, preferably a config file so that if they change the other application you don't have to recompile yours.

Regarding your first question I will always use a defined type to store the strings simply to have one location for change if needed.
So for your example i would have the following
public sealed class RootTypes
{
public const string Entity = "entity";
public const string UAV = "uav";
}
Your code then updates to this
typeOfObject = typeOfObject.ToLower();
if (typeOfObject == RootTypes.UAV)
{
stkUavObject = _stkObjectRootToIsolateForUavs.CurrentScenario.Children[stkObjectName];
var group = (IAgDataProviderGroup) stkUavObject.DataProviders["Heading"];
var provider = (IAgDataProvider) group.Group["Fixed"];
IAgDrResult result = ((IAgDataPrvTimeVar) provider).ExecSingle(_stkObjectRootToIsolateForUavs.CurrentTime);
stkObjectHeadingAndVelocity[0] = (double) result.DataSets[1].GetValues().GetValue(0);
stkObjectHeadingAndVelocity[1] = (double) result.DataSets[4].GetValues().GetValue(0);
}
else if (typeOfObject == RootTypes.Entity)
{
IAgStkObject stkEntityObject = _stkObjectRootToIsolateForEntities.CurrentScenario.Children[stkObjectName];
var group = (IAgDataProviderGroup) stkEntityObject.DataProviders["Heading"];
var provider = (IAgDataProvider) group.Group["Fixed"];
IAgDrResult result = ((IAgDataPrvTimeVar) provider).ExecSingle(_stkObjectRootToIsolateForEntities.CurrentTime);
stkObjectHeadingAndVelocity[0] = (double) result.DataSets[1].GetValues().GetValue(0);
stkObjectHeadingAndVelocity[1] = (double) result.DataSets[4].GetValues().GetValue(0);
}
The issue of code redundancy has been anserwed by Restuta

Use enums with bit flags:
[Flags]
public enum MyFlags
{
SomeFlag = 0x1, // 001
OtherFlag = 0x2,// 010
ThirdFlag = 0x4 // 100
}
var firstObject = MyFlags.SomeFlag;
var secondObject = MyFlags.SomeFlag | MyFlags.OtherFlag;
if(((int)secondObject & MyFlags.SomeFlag) != 0)
{
// true
}
if(((int)secondObject & MyFlags.OtherFlag) != 0)
{
// true
}
if(((int)firstObject & MyFlags.SomeFlag) != 0)
{
// true
}
if(((int)firstObject & MyFlags.OtherFlag) != 0)
{
// false
}
This article would be helpful.

Related

C#: how assign a value to a string var without using switch

First of all I must say that I am pretty new using C#. I have written this code block to assign a value to a string var depending on the value of another var. I have used the Switch statement:
switch (_reader.GetString(0))
{
case "G":
permiso.Area = "General";
break;
case "SIS":
permiso.Area = "Sistems";
break;
case "SOP":
permiso.Area = "Development";
break;
case "HLP":
permiso.Area = "Support";
break;
}
Can I make this in an easier way in C#?
Thanks!
You can use Dictionary<string, string>(), which can store your "switch case" string as key and "switch case value" in value.
Example:
var dict = new Dictionary<string, string>()
{
{"G", "General"},
{"SIS", "Sistems"},
...
}
So your code in order to access will be:
var key = _reader.GetString(0);
if(dict.TryGetValue(key, out var value)
{
permiso.Area = value;
}
else
{
// handle not exists key situation
}
Modern C# has a pattern matching switch
permiso.Area = _reader.GetString(0) switch {
"G" => "General",
"SIS" => "Sistems",
"SOP" => "Development",
"HLP" => "Support",
_ => throw new InvalidOperationException($"The value {_reader.GetString(0)} is not handled")
};
C# will complain at you if you don't include the "else" at the end _ =>
I mean if exists in C# somo sentence that makes something like that: my_string=my_string.decode(old_value0,new_value0, old_value1,new_value1, ...)
If you're after something like Oracle's DECODE, you can write it:
string Decode(string expr, params string[] arr){
for(int i = 0; i < arr.Length; i+=2)
if(arr[i] == expr)
return arr[i+1];
return arr.Length % 2 == 0 ? null : arr[arr.Length-1];
}
You'd use it like:
permiso.Area = Decode(reader.GetString(0), "G", "General", "SIS", "Sistems", "SOP", "Development", "HLP", "Support");
If you want an ELSE, pass an odd length array (something after the "Support")
If you want to be able to call it on a string, such as reader.GetString(0).Decode("G" ...) you can declare it in a static class and precede the first argument with this :
static string Decode(this string expr, ....)
That will make it an extension method, so it can be called "on a string"
If you either have same mappings occur on multiple locations or generally use the same strings repeatingly (in conditions, e.g.), a more sophisticated and cleaner approach would be to use enums and their descriptions in the first place. This makes the code more readable as you can (and should) use the unique enum in the code and catch its description when needed.
You need this enum extension method to read enum descriptions:
using System;
using System.ComponentModel;
public static string Description(this Enum source) {
DescriptionAttribute[] attributes = (DescriptionAttribute[])source
.GetType()
.GetField(source.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : string.Empty;
}
Prepare the enum and its corresponding dictionary mappers. If possible, those enums should only have one unique description but you can define additional mapper dictionaries to your likings, e.g. when you need a simple short to long text mapper as in your example.
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
// enum and its description
public enum PermissionArea {
[Description("Development")]
Development = 1,
[Description("General")]
General,
[Description("Sistems")]
Sistems,
[Description("Support")]
Support
}
public static class MyEnumDicts {
// default mapping of enum to its description
public static readonly Dictionary<PermissionArea, string> PermissionAreaToText = new Dictionary<PermissionArea, string>() {
{ PermissionArea.Development, PermissionArea.Development.Description() },
{ PermissionArea.General, PermissionArea.General.Description() },
{ PermissionArea.Sistems, PermissionArea.Sistems.Description() },
{ PermissionArea.Support, PermissionArea.Support.Description() }
};
// mapping of enum to short text
// (only if needed as it is better to only use one unique
// value which is already set as description in the enum itself
public static readonly Dictionary<PermissionArea, string> PermissionAreaToShortText = new Dictionary<PermissionArea, string>() {
{ PermissionArea.Development, "SOP" },
{ PermissionArea.General, "G" },
{ PermissionArea.Sistems, "SIS" },
{ PermissionArea.Support, "Support" }
};
// add reverse mappers via linq
public static readonly Dictionary<string, PermissionArea> TextToPermissionArea = PermissionAreaToText.ToDictionary(m => m.Value, m => m.Key);
public static readonly Dictionary<string, PermissionArea> ShortTextToPermissionArea = PermissionAreaToShortText.ToDictionary(m => m.Value, m => m.Key);
}
The usage could be as follows:
public void MyMethod(string permissionAreaShortText) {
try {
// map to enum (no switch or ifs etc. needed here)
PermissionArea permissionArea = MyEnumDicts.ShortTextToPermissionArea[permissionAreaShortText];
// now you can work via enums and do not
// have to hassle with any strings anymore:
switch (permissionArea) {
case PermissionArea.Development: ...; break;
case PermissionArea.General: ...; break;
case PermissionArea.Sistems: ...; break;
case PermissionArea.Support: ...; break;
}
// output/use its description when needed:
string permissionAreaText = permissionArea.Description();
// ...
} catch (Exception ex) {
// error handling: short text is no permission area
// ...
}
}

Roslyn SyntaxTree - Changing Field value

using the Roslyn SyntaxTree API, I'd like to replace the literal value "UV254" with a new value.
Example:
public class Analog
{
public const string Value = "UV254";
}
After update
public class Analog
{
public const string Value = "UV220";
}
I came up with the below solution but I suspect this could be simplified:
string sourceCode = "public class Analog { public const string Value = \"UV254\"; public const string Description = \"A Description\";}";
SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
CompilationUnitSyntax syntaxRoot = syntaxTree.GetCompilationUnitRoot();
LiteralExpressionSyntax afterLiteralExpressionSyntax = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal("UV220"));
LiteralExpressionSyntax beforeLiteralExpressionSyntax = null;
foreach (VariableDeclarationSyntax variableDeclarationSyntax in syntaxRoot.DescendantNodes().OfType<VariableDeclarationSyntax>())
{
foreach(VariableDeclaratorSyntax variableDeclaratorSyntax in variableDeclarationSyntax.Variables)
{
if(variableDeclaratorSyntax.Identifier.ValueText == "Value")
{
beforeLiteralExpressionSyntax = variableDeclaratorSyntax.DescendantNodes().OfType<LiteralExpressionSyntax>().Single();
break;
}
}
if(beforeLiteralExpressionSyntax != null)
{
break;
}
}
var newRoot = syntaxRoot.ReplaceNode(beforeLiteralExpressionSyntax, afterLiteralExpressionSyntax);
var fixedTree = newRoot.SyntaxTree.WithRootAndOptions(newRoot, syntaxTree.Options);
Can this be simplified?
Thanks for the help.
I think you can use some LINQ to shorten the determination of beforeLiteralExpressionSyntax. You could write the following instead:
LiteralExpressionSyntax beforeLiteralExpressionSyntax =
syntaxRoot.DescendantNodes().OfType<VariableDeclarationSyntax>()
.SelectMany(decl => decl.Variables)
.FirstOrDefault(declarator => declarator.Identifier.ValueText == "Value")
?.DescendantNodes().OfType<LiteralExpressionSyntax>()
.Single();
This will assign a null value to beforeLiteralExpressionSyntax if the field wasn't found. If you're sure the field will always be there, you could replace FirstOrDefault with First and replace the ?. with ..
Other than that, I don't think there is much you can do to simplify the code. My experience of working with Roslyn is that ultimately it is quite complicated to navigate through the syntax tree, pick out relevant bits of it and make changes to it, but I guess some of that is inevitable because it reflects the complexity of the C# language.

How to assign different enums to a variable in C#

I'm trying to do something like this..
enum Birds {
Crow,
Sparrow,
Hawk
}
enum Bugs {
Ant,
Spider,
Scorpion
}
if (featherless == true) {
var beast = Bugs;
} else {
var beast = Birds;
}
string a = (beast)2.ToString();
I have five lines of code that works on the provided enum. I can't imagine that I have to duplicate these five lines.. something like the above must be able to be used... The above structure of course causes beast to be out of scope and I can't pass var as a parameter. I know this is a headsmack moment, but I've searched and can't find a similar question.
var beastType = featherless ? typeof(Bugs) : typeof(Birds);
var a = Enum.GetValues(beastType).GetValue(2).ToString();
Will assign Hawk if featherless is true or Scorpion if it's false.
Edit 0
Maybe this will be a better solution if you only need the name:
var beastType = featherless ? typeof(Bugs) : typeof(Birds);
var a = Enum.GetName(beastType, 2);
Edit 1
Maybe you can try this if you need an object for further operations:
var beastType = featherless ? typeof(Bugs) : typeof(Birds);
var beast = Enum.ToObject(beastType, 2);
var a = beast.ToString();

Dynamically Generate Codes in C#

I have a list of Enums like the following:
public enum Evaluation : int
{
//Section 1
S1_1_1 = 579,
S1_1_2 = 584,
S1_1_3 = 589,
S1_1_4 = 594,
S1_1_5 = 599,
S1_1_6 = 604,
//Section 2
S1_2_1 = 610,
S1_2_2 = 615,
S1_2_3 = 620,
S1_2_4 = 625,
S1_2_5 = 630,
};
I want to iterate each section and use the values dynamically
int S1Count = 6;
for (int i = 1; i <= S1Count; i++)
{
VoteCount += string.IsNullOrEmpty(this.GetEvaluationValue(FormID, Evaluation.S1_1_ + i)) ? 0 : 1;
}
How can I achieve that? Thanks.
Sorry, my mistake. I tried to get the value from the database by using enum values which are IDs and I have to calculate counts, average for each section.
You can use Enum.Parse to do what you want I think though I don't reccomend it.
To use enum.Parse you'd just need to do something like:
Enum.Parse(typeof(Evaluation), String.Format("S1_1_{0}",i));
This does point at you using some dodgy methodology though. As I said in comments above you would be better off with a data structure allowing you to have sections and their contents easily differentiated. You can do this with either custom classes or maybe just a dictionary of Lists of ints...
Dictionary<int, List<int>> SectionContents;
and use it like:
foreach(int id in SectionContents[sectionNumber])
{
VoteCount += string.IsNullOrEmpty(this.GetEvaluationValue(FormID, id)) ? 0 : 1;
}
(I don't vouch for what's in the foreach, I'm just demonstrating how a dictionary of a list of ints could work).
Creating the Dictionary is easy enough and doesn't require enums. And if this is database stuff could easily be generated through a database query to get the IDs and what sections they are in and then create the data structure.
This will do it
public class Program
{
public static void Main()
{
foreach (FieldInfo fInfo in typeof(Evaluation).GetFields(BindingFlags.Public | BindingFlags.Static))
{
Console.WriteLine("Evaluation." + fInfo.Name);
}
Console.ReadLine();
}
}

Most efficient way to parse a flagged enum to a list

I have a flagged enum and need to retrieve the names of all values set on it.
I am currently taking advantage of the enum's ToString() method which returns the elements comma-separated.
public void SetRoles(Enums.Roles role)
{
IList<Entities.Role> roleList = role.ToString("G").Split(',')
.Select(r => new Entities.Role(r.Trim()))
.ToList();
...
}
I'm sure there must be a better way than this.
Try this:
public void SetRoles(Enums.Roles role)
{
List<string> result = new List<string>();
foreach(Roles r in Enum.GetValues(typeof(Roles)))
{
if ((role & r) != 0) result.Add(r.ToString());
}
}
If you genuinely just want the strings, can't get much simpler than:
string[] flags = role.ToString().Split(',');
This is simpler than using LINQ and is still just a single line of code.
Or if you want a list instead of an array as in the sample in the question you can convert the array into a list:
List<string> flags = new List<string>(role.ToString().Split(','));
In my case I needed a generic solution and came up with this:
value.ToString().Split(',').Select(flag => (T)Enum.Parse(typeof(T), flag)).ToList();
Enum.Parse will handle the concatenated values outputted by ToString just fine. Proof using the Immediate window:
? System.Enum.Parse(typeof(System.AttributeTargets), "Class, Enum")
Class | Enum
(the second line is the output, which is different in the debugger/immediate window from the generic Enum.ToString() output).
List<string> GetRoleNames(Roles roles) =>
Enum.GetValues(typeof(Roles))
.Cast<Roles>()
.Where(role => roles.HasFlag(role))
.Select(role => role.ToString())
.ToList();
void TestRoleSelection()
{
var selectedRoles = (Roles)6;
var roleNames = GetRoleNames(selectedRoles);
Console.WriteLine(string.Join(",", roleNames));
// Output: Admin,User
}
[Flags]
enum Roles
{
SuperAdmin = 1,
Admin = 2,
User = 4,
Anonymous = 8
}
Why do you need a list? Everything is already stored in the flags:
[Flags]
enum Roles
{
Read = 0x1,
Write = 0x2,
Delete = 0x4,
}
Then assign roles:
var roles = Roles.Read | Roles.Write;
And whenever you need to check if a given role has been you don't need to look in a list, but simply look in the roles enumeration:
if ((roles & Roles.Read) == Roles.Read)
{
// The user has read permission
}
if ((roles & Roles.Write) == Roles.Write)
{
// The user has write permission
}
Similar answer to Mick's but puts the operations into extensions and fixes/cleans up the extra space character (from the split).
Also as a bonus if the enum has a _ in it, the code changes it to a space.
public static class EnumExtensions
{
// Take anded flag enum and extract the cleaned string values.
public static List<string> ToComparableStrings(this Enum eNum)
=> eNum.ToString()
.Split(',')
.Select(str => str.ToCleanString())
.ToList();
// Take an individual enum and report the textual value.
public static string ToComparableString(this Enum eNum)
=> eNum.ToString()
.ToCleanString();
// Remove any spaces due to split and if `_` found change it to space.
public static string ToCleanString(this string str)
=> str.Replace(" ", string.Empty)
.Replace('_', ' ');
}
Usage
var single = PivotFilter.Dollars_Only;
var multiple = PivotFilter.Dollars_Only | PivotFilter.Non_Productive;
// These calls return:
single.ToComparableString() // "Dollars Only"
multiple.ToComparableString() // "Non Productive,Dollars Only"
multiple.ToComparableStrings() // List<string>() { "Non Productive", "Dollars Only" }
Enum for Usage
[Flags]
// Define other methods, classes and namespaces here
public enum PivotFilter
{
Agency = 1,
Regular = 2,
Overtime = 4,
Non_Productive = 8,
Dollars_Only = 16,
Ignore = 32
}
Turning a flagged enum to a list might not be as straight forward as it looks. Take the following enum for example:
[Flags]
enum MenuItems
{
None = 0,
Pizza = 1,
Fries = 2,
Pancakes = 4,
Meatballs = 8,
Pasta = 16,
StuffWithP = Pizza | Pancakes | Pasta,
All = Pizza | Fries | Pancakes | Meatballs | Pasta | StuffWithP
};
If we have the value StuffWithP, what do we want in the list? StuffWithP or Pizza, Pancakes, Pasta? I had a use case in witch I needed to "deconstruct" the enum value to the invidual flags and put those in a list. I came up with the following:
public static string[] DeconstructFlags(Enum items)
{
if (items.GetType().GetCustomAttribute<FlagsAttribute>() == null)
{
throw new ArgumentException("Enum has no [Flags] attribute.", nameof(items));
}
// no value, no list
var itemsValue = (int)(object)items;
if (itemsValue == 0) return Array.Empty<string>();
var result = new List<string>();
foreach (var item in Enum.GetValues(items.GetType()))
{
if(item == null) continue;
var value = (int)item;
// skip combined flags
if (!BitOperations.IsPow2(value))
{
continue;
}
if (items.HasFlag((Enum)item))
{
result.Add(item.ToString() ?? "");
}
}
return result.ToArray();
}
I don't know if it is the most efficient, but it skips those combined flags nicely. Wrote some more on my blog: Deconstructing a [Flags] enum.
F# version
module Enum =
let inline flagsToList< ^e when ^e: equality and ^e: (static member (&&&): ^e * ^e -> ^e)> value =
Enum.GetValues(typeof<^e>)
|> Seq.cast<^e>
|> Seq.where (fun case -> case &&& value = case)
|> Seq.toList

Categories