I have the following code:
switch (pk.Substring(2, 2))
{
case "00":
ViewBag.Type = _reference.Get("14", model.Type).Value;
break;
case "01":
ViewBag.Type = _reference.Get("18", model.Type).Value;
break;
}
It does the job but does not look very clean to me. Is there some way I could make this code a bit smaller. I was thinking to just have the number 14 or 18 as a variable but I am not sure the best way to code if I should use if-else or some other way.
You could use a static dictionary as a map instead of a switch-statement.
static readonly Dictionary<string, string> map = new Dictionary<string, string> {
{ "00", "14" },
{ "01", "18" },
// ... more ...
};
// ... in your method ...
string str = pk.Substring(2, 2);
string val;
if (!map.TryGetValue(str, out val))
{
// Handle error, like in the "default:" case of the switch statement
}
else
{
ViewBag.Type = _reference.Get(val, model.Type).Value;
}
However, I would only do this, if there are really a lot of mappings that maybe can even be "read" from an external source like a configuration file.
Also note, that if the "key" is really a consecutive sequence of integers starting at 0, you might be able to use an array, where the "key" is simply the index into it.
static readonly string[] map = new string[] {
"14", "18", ...
};
int index = Int32.Parse(pk.Substring(2, 2)); // Error handling elided.
if (index < 0 || index > map.Length)
{
// Handle error, like in the "default:" case of the switch statement
}
else
{
ViewBag.Type = _reference.Get(map[index], model.Type).Value;
}
Otherwise rather stay with an explicit switch statement (possibly factoring out the assignment for more terse code):
string val;
switch (pk.Substring(2, 2))
{
case "00":
val = "14";
break;
case "01":
val = "18";
break;
// ... more ...
default:
// Error handling for unknown switch-value.
break;
}
ViewBag.Type = _reference.Get(val, model.Type).Value;
It seems that there is some relationship between "00"->"14" and "01"->"18". I believe this relationship results from the business logic. You should wrap the logic and make the code in your controller clear. Finally the code in the controller should look like:
public ActionResult MyAction()
{
//some code
ViewBag.Type = TypeProvider.GetType(pk, model.Type);
//return something
}
class TypeProvider
{
Dictionary<string, string> relations = ...
//a dictionary stores "00"->"14" logics
public static SomeType GetType(string pk, Type modelType)
{
return _reference.Get(relations[pk.SubString(2,2)], modelType).Value;
}
}
var data = pk.Substring(2, 2);
var choice = data == "00" ? "14" : (data=="01"?"18":"");
if (choice != string.Empty) ViewBag.Type = _reference.Get(choice, model.Type).Value;
I use mapping extensions fot that kind of code:
ViewBag.Type = pk.Substring(2, 2)
.Map("00", x => GetViewBagValue("14"))
.Map("01", x => GetViewBagValue("18"))
and in your case this method:
private ViewBagValue GetViewBagValue(string value)
{
return _reference.Get(value, model.Type).Value;
}
I use this. You could easily change it to generic or use e.g. object[] instead. Not super efficient, but very compact:
public static class Util {
public static string Switch(string value, params string[] nameValues) {
for (int x = 0; x < nameValues.Length; x += 2) {
if (nameValues[x] == value) {
return nameValues[x + 1];
}
}
return string.Empty;
}
}
Then just call that like this:
var res = Util.Switch("test2", "test1", "res1", "test2", "res2");
Best of luck!
Related
So I have a program for this class where I'm supposed to allow the user to select a data type like byte, int, short, long, and etc. And then I am supposed to validate and make sure that the numbers they enter for the math problem aren't outside of the bounds of the data type they selected. Right now I'm using a bunch of if statements for each individual data type, and checking if it's above MaxValue or below MinValue. However, there has to be a better way to do this, right?
My current code is like this (numType is a byte set to the value of a constant when the button is pressed):
if(numType = BYTE){
if(leftNum > byte.MaxValue){
errorsEncountered = true;
returnString = returnString + "Left number must not be more than " +
byte.MaxValue.ToString() + " for a byte.\n";
}
if(leftNum < byte.MinValue){
errorsEncountered = true;
returnString = returnString + "Left number must not be less than " +
byte.MinValue.ToString() + " for a byte.\n";
... (so on and so forth)
}
However I'd like to think that you could instead use something like a variable to record the data type and use that instead. So lets say that you have an array of each potential value of numType (1-7 in this case). Would there be a way to do something like this?
byte numType = 8; // Will never be returned as this, an error is output for this value immediately.
const byte BYTE = 0;
const byte SHORT = 1;
const byte INT = 2;
const byte LONG = 3;
const byte FLOAT = 4;
const byte DOUBLE = 5;
const byte DECIMAL = 6;
string[] dataTypes = {"byte", "short", "int", "long", "float", "double", "decimal"};
if(leftNum > dataTypes[numType].MaxValue) {
errorsEncountered = true;
returnString = "Left number must not be more than " +
dataTypes[numType].MaxValue.ToString() + " for a " + dataTypes[numType] + ".";
}
if(leftNum < dataTypes[numType].MinValue) {
errorsEncountered = true;
returnString = "Left number must not be more than " +
dataTypes[numType].MinValue.ToString() + " for a " + dataTypes[numType] + ".";
}
I know my demonstration is incredibly simplistic but I genuinely don't know how better to describe what I'm trying to do. Thank you for any help you can provide.
Edit: Honestly it seems I'm a bit out of my depth here. I have no clue what most of these solutions are actually doing, and I've come out of this with the impression that I should probably just work on learning the language as a whole.
You can keep a dictionary of all the types you want to use with the string you want the user to type in to refer to that type, then use reflection to call TryParse and MinValue/MaxValue if needed.
Something like this:
public static readonly Dictionary<string, Type> aliases = new() {
{ "byte", typeof(byte) },
{ "short" , typeof(short) },
{ "int" , typeof(int) },
{ "long" , typeof(long) },
{ "float" , typeof(float) },
{ "double" , typeof(double) },
{ "decimal" , typeof(decimal) }
};
static void Main() {
Type type;
while (true) {
Console.WriteLine("Enter the type:");
var selectedType = Console.ReadLine().Trim();
if (!aliases.TryGetValue(selectedType, out type)) {
Console.WriteLine("You did it wrong");
continue;
}
break;
}
while (true) {
Console.WriteLine("Type a value:");
var value = Console.ReadLine().Trim();
// Create an instance of whatever type we're using
object result = Activator.CreateInstance(type);
// Get a reference to the TryParse method for this type
var tryParseMethod = type.GetMethod("TryParse", new[] { typeof(string), type.MakeByRefType() });
// Call TryParse
if (tryParseMethod.Invoke(null, new[] { value, result }) is bool success && success) {
Console.WriteLine("You did it right!");
break;
} else {
// TryParse failed, so show the user the min/max values
var minValueProp = type.GetField("MinValue");
var maxValueProp = type.GetField("MaxValue");
Console.WriteLine($"You did it wrong. Enter a value between {minValueProp.GetValue(result)} and {maxValueProp.GetValue(result)}");
continue;
}
}
}
For example:
Enter the type:
byte
Type a value:
-1
You did it wrong. Enter a value between 0 and 255
Type a value:
1
You did it right!
A bit more of code would be helpful, for instance how leftNum and numType are declared.
Let's try to help anyway. You can try a polymorphic approach.
It may be overkill, but it's a nice exercise:
Create an interface, let's say ITypeValidator
Create the implementations of such Interface, i.e. IntegerValidator : ITypeValidator
Have a Dictionary containing your validators and their respective type Dictionary<byte, ITypeValidator>
Initialize your types in your Dictionary.
Sample:
internal class Program
{
static void Main(string[] args)
{
var myTypes = GetTypesImplementingInterface();
byte numType = 2;
object leftNum = 3;
var validator = myTypes[numType];
var result = validator.Validate(leftNum);
}
public static Dictionary<byte, ITypeValidator> GetTypesImplementingInterface()
{
// Fancy approach would be to use reflection to scan the Assembly
// for implementations of our interface and automatically
// instantiate and fill in the Dictionary
return new Dictionary<byte, ITypeValidator>()
{
{ 2, new IntValidator() }
};
}
public interface ITypeValidator
{
byte TypeNumber { get; } // Tip: this helps with the Reflection approach
ValidationResult Validate(object number);
}
public class ValidationResult
{
public bool HasError { get; set; }
public List<string> ErrorMessages { get; set; }
}
public class IntValidator : ITypeValidator
{
public byte TypeNumber => 2;
public ValidationResult Validate(object number)
{
// do your things
return new ValidationResult();
}
}
}
If your validations are always the same (min and max), you can also think of adding a base class, with abstract Minimum and Maximum properties and a base validation method. So for each type, you just need to implement the TypeNumber, Min and Max values.
Currently i am having code like this inside dataGridView1_CellValidating event:
if(e.ColumnIndex == dataGridView1.Columns["FIRST"].Index)
{
// Some code
}
else if(e.ColumnIndex == dataGridView1.Columns["Second"].Index)
{
// Some code
}
else if(e.ColumnIndex == dataGridView1.Columns["Third"].Index)
{
// Some code
}
And it is like this because i cannot use it in switch statement like:
switch(e.ColumnIndex)
{
case dataGridView.Columns["First"].Index:
break;
case dataGridView.Columns["Second"].Index:
break;
case dataGridView.Columns["Third"].Index:
break;
}
returns me error on case line Expecting constant value.
So how can i make this work?
The switch statement is complaining because the “case” portion of the statement “requires” a “CONSTANT” value. The statement dataGridView.Columns["First"].Index will always return the same value… unless you move the column… which you can do. This is why the compiler is going to look at the retuned value from dataGridView.Columns["First"].Index as NOT a “constant”.
This makes sense in the fact that the “column index” for the column named “First” could be at ANY column index in the grid… Hence the error.
A possible solution is to grab the current columns “Name” string value then switch off the column “Name” like below.
string columnName = dataGridView.Columns[e.ColumnIndex].Name;
switch (columnName) {
case "First":
MessageBox.Show("Cell Validated is in 'FIRST' column");
break;
case "Second":
MessageBox.Show("Cell Validated is in 'Second' column");
break;
case "Third":
MessageBox.Show("Cell Validated is in 'Third' column");
break;
}
If you really want to use switch you can make use of pattern matching in switch case
PS: For C# 7.0 or above
switch(e.ColumnIndex)
{
case var _ when (dataGridView.Columns["First"].Index == e.ColumnIndex):
break;
case var _ when (dataGridView.Columns["Second"].Index == e.ColumnIndex):
break;
case var _ when (dataGridView.Columns["Third"].Index == e.ColumnIndex):
break;
}
Maybe, you first make constant values and assign dataGridView.Columns["First"].Index to it.
For example:
int a = {given index}
const int IndexOfFirstCol = dataGridView.Columns["First"].Index;
const int IndexOfSecCol = dataGridView.Columns["Second"].Index;
then,
switch(a)
{
case IndexOfFirstCol:
//do smth
break;
case IndexOfSecCol:
//do smth
break;
}
If you cannot use pattern matching from C# 7.0, there is also another way by using dictonaries where your keys are functions checking the conditions (cases) and the values are the actions you want to perform. For your code it would look like:
private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
{
var caseDictionary = new Dictionary<Func<bool>, Action>()
{
{ () => (e.ColumnIndex == dataGridView1.Columns["First"].Index), () => { MessageBox.Show("First");}},
{ () => (e.ColumnIndex == dataGridView1.Columns["Second"].Index), () => { MessageBox.Show("Second");}},
{ () => (e.ColumnIndex == dataGridView1.Columns["Third"].Index), () => { MessageBox.Show("Third");}}
};
caseDictionary.Where(caseRecord => caseRecord.Key()).Select(action => action.Value).FirstOrDefault()?.Invoke();
}
You could of course declare the Dictionary in your constructor and just call it in the CellValidating event.
I would have another approach, using a Dictionnary (from the namespace System.Collections.Generic) of methods built in such way
The key is the index of the column in the datagridview ("First", "Second" ...)
The value is a Delegate to the method to do (what replaces your // some code in each if/else if
In example :
/*
* This example is written for console application, that can be tested easily.
* The logic can be rewritten for WinForm
*/
static void TheFirstCase()
{
//This should be replaced by the differents actions you want to do
Console.WriteLine("first case");
}
static void TheSecondtCase()
{
Console.WriteLine("second case");
}
static void TheThirdCase()
{
Console.WriteLine("third case");
}
static void Main(string[] args)
{
Dictionary<string, Delegate> MyDic = new Dictionary<string, Delegate>
{
//If you need parameters in the TheFirstCase(), use new Action<TypeOfTheFirstParam, TypeOfTheSecondParam, ...>(TheFirstCase)
//If your Method needs to return something, use Func instead of Action
{ "First", new Action(TheFirstCase) },
{ "Second", new Action(TheSecondtCase) },
{ "Third", new Action(TheThirdCase) }
};
// in your question, this is e.ColumnIndex
var ValueInColIndex = 42;
// in your question, this is dataGridView.Columns
var DataGridViewDatas = new Dictionary<string, int>
{
{ "First", 0 },
{ "Second", 42 },
{ "Third", 69 }
};
foreach (var MyAction in MyDic)
{
if (DataGridViewDatas[MyAction.Key] == ValueInColIndex)
{
MyAction.Value.DynamicInvoke();
}
}
}
Outputs :
second case
I'm trying to check if a user input matches a few already known values. How do I do this in C#?
I've tried this:
if (UserInput.Text == "2", "4", "8", "16", "32")
{ do things
}
else
{
do other things
}
And a couple of other signs between my strings. Is it possible to check against all my strings or do I have to set up a seperate if-statment for all my strings?
You can use a switch to compare a string to multiple values:
switch (UserInput.Text) {
case "2":
case "4":
case "8":
case "16":
case "32":
// do things
break;
default:
// do other things
break;
}
You can put the strings in a hash set, and check if the hash set contains the user input:
HashSet<string> strings = new HashSet<string>();
strings.Add("2");
strings.Add("4");
strings.Add("8");
strings.Add("16");
strings.Add("32");
if (strings.Contains(UserInput.Text)) {
// do things
} else {
// do other things
}
You could use an array:
string[] validValues = new string[] {"2", "4", "8", "16", "32"}
if (validValues.Contains( UserInput.Text))
{ do things
}
else
{
do other things
}
This way you get a nice, readable sollution.
I like extensions methods for these situations
public static class StringExtensions
{
public static int MatchWord(this string container, bool caseInsensitive, params string[] values)
{
int result = -1;
int counter = 0;
foreach (string s in values)
{
if (s != null && string.Compare(container, s, caseInsensitive) == 0)
{
result = counter;
break;
}
counter++;
}
return result;
}
}
Now you could write (whenever you need it)
int matchIndex = UserInput.Text.MatchWord(false, "2", "4", "8", "16", "32");
if(matchIndex != -1)
{
// string is equal to one of the input
}
else
{
}
but you could also use this extension for
int matchIndex = UserInput.Text.MatchWord(true, "john", "ron", "mark", "james", "bob");
If you don't need the index of the matched word, you could simplify everything using a boolean for the return value of the extension method
You could make an array of what you want to check against and just check if the array contains an elements that is the same as UserInput.Text.
Want a code example?
I know the following solution is not good. But I am trying to do the same with If statement
if (UserInput.Text == "2"|| UserInput.Text =="4"||UserInput.Text == "16")
{ do things
}
else
{
do other things
}
I'm basically saving and loading values into an array to feed into a JSON library, and I'm just looking for a more elegant way to do this:
class properties to array
return new object[] { path, pathDir, name };
array to class properties
c.path = values[0];
c.pathDir = values[1];
c.name = values[2];
Simple solutions that ideally do not require additional run time overheads such as Reflection are appreciated.
Can I do something like this?
c.{path, pathDir, name} = values[0...2]
Edit: I'm specifically asking for arrays. I know about serialization and JSON and Protobuf and everything else everyone is suggesting.
Would this not do the trick?
return new {path= "/Some/Path", pathDir= "SiteRoot", name="MyPath"}
Edit:
//Mock function to simulate creating 5 objects with 'CreateArrayOb' function
public void CreatingObjects()
{
var lst = new List<object>();
for (var x = 0; x < 5; x++)
{
lst.Add(CreateArrayOb(new string[] {"Path" + x, "Dir" + x, "Path" + x}));
}
}
public object CreateArrayOb(object[] vals)
{
if (vals != null && vals.Any())
{
//Switch cases in the event that you would like to alter the object type returned
//based on the number of parameters sent
switch (vals.Count())
{
case 0:
break;
case 1:
break;
case 2:
break;
case 3:
return new { path = vals.ElementAt(0), pathDir = vals.ElementAt(1), name = vals.ElementAt(2) };
}
}
return null;
}
for example
if (x=="A)
switch (y)
{
case "1": Do1();break;
case "2": Do2();break;
case "3": Do3();break;
}
else if (x=="B")
switch (y)
{
case "1": Do4();break;
case "2": Do5();break;
case "3": Do6();break;
}
else
switch (y)
{
case "1": Do7();break;
case "2": Do8();break;
case "3": Do9();break;
}
I wish I could do the following, however it has many redundant checks.
if (x=="A" && y=="1")
Do1();
else if (x=="A" && y=="2")
Do2();
else if (x=="A" && y=="3")
Do3();
else if (x=="B" && y=="1")
Do4();
else if (x=="B" && y=="2")
Do5();
else if (x=="B" && y=="3")
Do6();
else if (x=="C" && y=="1")
Do7();
else if (x=="C" && y=="2")
Do8();
else if (x=="C" && y=="3")
Do9();
Suggestion to introduce OOPS is really great, please do not ignore that comment. For time being you can write your code like this.
var combinedText = x+y;
switch(combinedText)
{
case "A1": Do1(); break;
case "A2": Do2(); break;
case "A3": Do3(); break;
case "B1": Do4(); break;
case "B2": Do5(); break;
case "B3": Do6(); break;
case "C1": Do7(); break;
case "C2": Do8(); break;
case "C3": Do9(); break;
}
Your code currently has two responsibilities - deciding what set of methods to execute (varible x) and deciding which exact method to execute (varible y). Simplest option to make code much more clear - split this responsibilities and extract methods, that will decide which method from set of methods to call
switch (x)
{
case "A": DoA(y); break;
case "B": DoB(y); break;
default:
DoDefault(y); break;
}
Now your caller code is simple. And here is one of DoX methods:
private void DoA(string y)
{
switch (y)
{
case "1": Do1(); break;
case "2": Do2(); break;
case "3": Do3(); break;
}
}
Other option is to make .net to decide which set of methods to call, by using polymorphism. But in your simple case with only one switch(x) block, I will not recommend to do that. If your real code is more complex, then consider to extract classes which will hold set of functionality (Do1, Do2, Do3) and will decide upon that functionality execution. E.g. calling code:
IDo ido = CreateIDo(x);
ido.Do(y);
Yes, that's all. Extremely clean. Here is IDo interface creation code:
public static IDo CreateIDo(string x)
{
switch (x)
{
case "A": return new A();
case "B": return new B();
default:
return new C();
}
}
And here is class A, that encapsulates first set of methods and decisions upon executing them:
public interface IDo
{
void Do(string y);
}
public class A : IDo
{
public void Do(string y)
{
switch (y)
{
case "1": Do1(); break;
case "2": Do2(); break;
case "3": Do3(); break;
}
}
private void Do1() { }
private void Do2() { }
private void Do3() { }
}
Again, use this in case your real code is more complex.
I would use an IEnumerable collection of Tuples and an Action delegate to define your list of methods to be called, create the list as a private field or in the class initialiser, or to be flexible you can add Tuples to a public property as needed. If you need to pass in parameters use one of the overloaded versions of the Action delegate ie: Action(t1, t2) etc.
If you need a return value use the Func delegate as per the other answer.
IEnumerable<Tuple<string, string, Action>> actions = new List<Tuple<string, string, Action>>() {
Tuple.Create<string, string, Action>("A", "1", SomeMethod1),
Tuple.Create<string, string, Action>("A", "2", SomeMethod2)
};
string x = "A";
string y = "2";
var action = actions.FirstOrDefault(t => ((t.Item1 == x) && (t.Item2 == y)));
if (action != null)
action.Item3();
else
DoSomeDefaultMethod();
public void SomeMethod1() { // Whatever you want to do }
public void SomeMethod2() { // Whatever you want to do }
public void DoSomeDefaultMethod() { // Default Method }
void Main()
{
Dictionary<string, Action> d = new Dictionary<string, Action>()
{
{"A1", Do1},
{"A2", Do2},
{"A3", Do3},
{"B1", Do4},
{"B2", Do5},
{"B3", Do6},
{"1", Do7},
{"2", Do8},
{"3", Do9}
};
var x = "A";
var y = "1";
var action = x == "A" || x == "B" ? x + y : y;
if (d.ContainsKey(action))
d[action]();
}
public void Do1() {}
public void Do2() {}
public void Do3() {}
public void Do4() {}
public void Do5() {}
public void Do6() {}
public void Do7() {}
public void Do8() {}
public void Do9() {}
EDIT
I remembered about this fluent functional switch:
var sw = new Switch<string>(action)
.Case("A1", s => Do1())
.Case("A2", s => Do2());
Consider this if you don't want to change much of your current structure,(and don't want to create new types etc.)
Add them to tuples like below
var tuples = new List<Tuple<string,string,Func<>>()>(); // Func should be of your Do() type
Add your conditional data with the related funcs to the list
tuples.Add(new Tuple<string,string,Func<>>("A","1", Do1()));
...
Just call it when required using your conditionals directly
var function = tuples.Where(x => x.item1 == "A" && x.item2 == "1").Select(x => x.item3);
function.Invoke(); // to call it.
Now if you got more conditionals in future, you can just add them to the list without changing any code.
Use some thing like this . only three if would do.
if (x == "A")
{
int a = (y == "1") ? do1() : ((y == "2") ? do2() : do3());
}
}
int do1() { return 10; }
int do2() { return 10; }
int do3() { return 10; }
I guess the same kind of switch on X is performed in more than one place in your code, if so kindly refactor it and use polymorphism instead
If X is string first replace the typecode with class and use polymorphism.