string to constant type in c# - c#

We have constants defined in a static class along with functions like
public const string MSG_1 = "New error {0}"
public const string MSG_2 = "Random error occurred {1}"
public static string Message_ToForm(string MType, string[] vals)
public static GetNewType(string MType)
{
switch (MType)
{
case "MSG_1" : ...........
}
}
I require to call it from a program like
Message_ToForm("MSG_1", string[]);
How can I convert it to get the value of the constant from string type?
Basically, it should return me as "New error {0} when "MSG_1" is passed?

You might need a return type as String for your GetNewType
Suggestion:
If the constants are NOT reused and it if its ONLY for your lookup.
You could use a Dictionary to do the lookup
Dictionary<string, string> constValues = new Dictionary<string, string>()
{
{"MSG_1", "New error {0}"},
{"MSG_2", "Random error occurred {1}"}
};
public string GetNewType(string MType)
{
if (constValues.ContainsKey(MType))
return constValues[MType];
return string.Empty;
}

I'm really confused with your question, but think that's what you're looking for:
public static string GetNewType(string MType)
{
switch (MType)
{
case "MSG_1": return MSG_1;
case "MSG_2": return MSG_2;
default: throw new ArgumentException("MType");
}
}
But I must say - that's really bad approach! You should rethink your design.

I would create a MessageType enumeration and switch based on that.
enum MessageType
{
None = 0,
Msg1,
Msg2
}
public static string GetNewType(MessageType MType)
{
string msg = string.Empty;
switch (MType)
{
case MessageType.Msg1:
msg = MSG_1;
break;
case MessageType.Msg2:
msg = MSG_2;
break;
}
return msg;
}

You were missing a return type on your method. I believe this is what you want.
static string GetNewType(string MType)
{
switch (MType)
{
case "MSG_1" :
return MSG_1;
case "MSG_2":
return MSG_2;
}
return "";
}
But is there are reason your strings are saved as constants in variables? Couldn't you just return the strings in the switch? Like this:
switch (MType)
{
case "MSG_1" :
return "New error {0}";
case "MSG_2":
return "Random error occurred {1}";
}

Create a static readonly ReadOnlyDictionary property populated with your 'constants':
private static readonly System.Collections.ObjectModel.ReadOnlyDictionary<string, string> _msgDictionary =
new System.Collections.ObjectModel.ReadOnlyDictionary<string, string>(
new Dictionary<string, string>()
{
{ "MSG_1", "New error {0}" },
{ "MSG_2", "Random error occurred {1}" }
});
public static System.Collections.ObjectModel.ReadOnlyDictionary<string, string> Messages
{
get { return _msgDictionary; }
}
Then usage:
var msg = Messages["MSG_1"];

public static GetNewType(string MType)
{
string MType = yourClass.MSG_1
switch (MType)
{
case yourClass.MSG_1
break;
....
default:
// Code you might want to run in case you are
// given a value that doesn't match.
break;
}
}

I strongly recommend using the out of the box .NET localization feature. You simply add a resource file to your project and put all your string messages into it, the resource file is like a key value pair of string resources, the IDE automatically creates a property for each string message which you can use in your code.
Check this How to use localization in C# for more details.

Related

C# manipulating data parsed from CSV

I'm creating a program to generate schematics based off of user input. This has to be done dynamically/by hand due to the sheer volume of different possibilities (6.8M, growing exponentially). Right now I'm working on importing some data via CSV.
Example data:
Type,TIN_pos,TIN_ID,Desc
Elect, 0, X, Manual Regulator
Elect, 0, A, Electronic Regulator
Import code:
List<TIN_Fields> values = File.ReadAllLines("C:\\Users\\User\\Desktop\\Visual Basic\\CSV_Test_1.csv")
.Skip(1)
.Select(v => TIN_Fields.FromCsv(v))
.ToList();
public class TIN_Fields
{
public string Type;
public int TIN_pos;
public string TIN_ID;
public string Desc;
public static TIN_Fields FromCsv(string csvLine)
{
string[] values = csvLine.Split(',');
TIN_Fields _Fields = new TIN_Fields();
_Fields.Type = Convert.ToString(values[0]);
_Fields.TIN_pos = Convert.ToInt16(values[1]);
_Fields.TIN_ID = Convert.ToString(values[2]);
_Fields.Desc = Convert.ToString(values[3]);
return _Fields;
}
}
Once that data is Imported, I need to do two things with it,
display the raw csv data in a ListView table, just so users can see if anything in the list needs updating.
be able to compare the items in the list to various characters in a 10-digit hexadecimal code, and spit out some results.
First and foremost, i need to run through the list that was created with the above code, make sure that:
TIN_pos value = 0
because that is the character position of the input box.
Then, with the remaining options, look for the character represented in the input in the TIN_ID field.
Once found, it should then output the Desc field.
Everywhere I have looked says to use foreach, but that requires the array name, which is the part that is confusing me. I've tried filling in basically all of the variables in the FromCSV Method and usually get an error that the class doesn't have a definition.
to hopefully clear up confusion with my explanation, here is the code I created that does the same thing, but with the CSV data hard coded into it, using switch cases and if statements.
public partial class Form1 : Form
{
public string Model_Chassis;
public string Model_Test_Type;
public int ChannelNumberVar => Convert.ToInt32(TextBox_TIN[2]);
public string Tester_Type_Selector;
public string TextBox_TIN
{
get { return TIN_Entry_TextBox.Text; }
set { TIN_Entry_TextBox.Text = value; }
}
public string Model_Data_D
{
get { return Model_Data.Text; }
set { Model_Data.Text = value; }
}
public Form1()
{
InitializeComponent();
}
//Method grabs TIN Box data and decodes it to model information.
public void Model_Select()
{
//Picks Model Chassis
switch (char.ToUpper(TextBox_TIN[0]))
{
case 'H':
{
Model_Chassis = Coding.Model1.description;
}
break;
default:
{
Model_Data_D = "INVALID TIN";
}
break;
}
//Picks Test Type
switch (char.ToUpper(TextBox_TIN[3]))
{
case '0':
{
Model_Test_Type = Test_Types.TT_PD.TT_tt;
}
break;
case '1':
{
Model_Test_Type = Test_Types.TT_PV.TT_tt;
}
break;
default:
{
Model_Test_Type = "";
}
break;
}
//Puts chassis and Test Type together
if (Model_Data_D.Equals("INVALID TIN"))
{
;
}
else if (char.ToUpper(TextBox_TIN[2]).Equals(Coding.Num_Chan_1_2.tin_id))
{
Model_Data_D = $"{Model_Chassis}-{Model_Test_Type}";
}
else
{
Model_Data_D = $"{Model_Chassis}-{TextBox_TIN[2]}{Model_Test_Type}";
}
}
public class Coding
{
public char tin_id;
public string description;
public Coding(char TIN_ID, string Desc)
{
tin_id = TIN_ID;
description = Desc;
}
public static Coding Model1 = new Coding('H', "Model1");
public static Coding Num_Chan_1_2 = new Coding('X', "Single Channel");
public static Coding Elect_Reg_F_1 = new Coding('X', "Manual Regulator");
}
}
INPUT:
HXX0X
OUTPUT
Model1-PD
Thanks in advance for the help!
You're asking quite a few questions, and providing a lot of extra details in here, but for this:
"First and foremost, i need to run through the list that was created with the above code, make sure that:
TIN_pos value = 0
because that is the character position of the input box."
(seeing as you say you need to do this 'first and foremost').
In your FromCsv method, check the value as you create the record, and throw an error if it is invalid. Like this:
public static TIN_Fields FromCsv(string csvLine)
{
string[] values = csvLine.Split(',');
TIN_Fields _Fields = new TIN_Fields();
_Fields.Type = Convert.ToString(values[0]);
_Fields.TIN_pos = Convert.ToInt16(values[1]);
if(_Fields.TIN_pos != 0){
throw new Exception("TIN_pos must be 0");
}
_Fields.TIN_ID = Convert.ToString(values[2]);
_Fields.Desc = Convert.ToString(values[3]);
return _Fields;
}
Assuming you've read in your CSV correctly, which it seems you have, then selecting the appropriate TIN from the list is a simple LINQ statement. The following code assumes that TIN IDs are unique and only a single character in length.
static void Main(string[] args)
{
string testCsv = #"C:\Users\User\Desktop\Visual Basic\CSV_Test_1.csv";
List<TIN_Fields> values = File.ReadAllLines(testCsv)
.Skip(1)
.Select(v => TIN_Fields.FromCsv(v))
.ToList();
// Simulates input received from form
string input = "HXX0X";
TIN_Fields selectedTIN = values.First(x => x.TIN_ID == Convert.ToString(input[0]));
// Insert the description as needed in your ouput.
string output = $"{ selectedTIN.Desc }-";
}
Hopefully that answers another part of the problem. The Convert.ToString() is required because the output of input[0] is a char.

Function parameters - how to use a pre-defined list instead of string value

This should be simple but I can't seem to figure it out. I have a function to send data to an API:
public bool UpdateCustomer(int customerid, string status)
{
apicall(customerid, status);
return true;
}
UpdateCustomer(1234, "shipped")
This works fine. But the API has a predefined list of acceptable statuses, and I don't want to leave it to the person calling the function to get it right, I want to create some sort of enum or something:
public bool UpdateCustomer(int customerid, Status status)
{
apicall(customerid, status);
return true;
}
UpdateCustomer(1234, Statuses.Shipped)
I want to pass the value to the API of their actual accepted value, "shipped".
I've tried creating a class with static strings:
public class Status
{
public static string Cancelled = "cancelled";
public static string Unsubmitted = "unsubmitted";
public static string Saved = "saved";
public static string Submitted = "submitted";
public static string Arrived = "arrived";
public static string Completed = "completed";
public static string Shipped = "shipped";
}
but when I go to call UpdateCustomer(1234, Status.Shipped), it says it can't convert from a string to Status. I've done this with enums plenty of times but I can't use an enum because those only support int values.
What is the easiest way of doing this with string values (or anything besides integers, really)?
Edit: It does work if I set the function to public bool UpdateCustomer(int customerid, string status) and call it with UpdateCustomer(1234, Statuses.Shipped) but that defeats the purpose of idiot-proofing this, you can still pass whatever string value you want. I really want the function parameter to be type of Status so it forces the caller to use one of the pre-defined statuses.
enum Status
{
Cancelled,
Saved,
..
}
public bool UpdateCustomer(int customerid, Status status)
{
apicall(customerid, status.ToString());
return true;
}
enum StatusEnum
{
Cancelled,
Unsubmitted,
Saved,
Submitted,
Arrived,
Completed,
Shipped
}
public bool UpdateCustomer(int customerid, StatusEnum status)
{
string statusStr;
switch(status)
{
case StatusEnum.Cancelled:
statusStr = "cancelled";
break;
case StatusEnum.Unsubmitted:
statusStr = "unsubmitted";
break;
case StatusEnum.Saved:
statusStr = "saved";
break;
case StatusEnum.Submitted:
statusStr = "submitted";
break;
case StatusEnum.Arrived:
statusStr = "arrived";
break;
case StatusEnum.Completed:
statusStr = "completed";
break;
case StatusEnum.Shipped:
statusStr = "shipped";
break;
case default:
throw new NotImplementedException();
}
apicall(customerid, statusStr);
return true;
}
This can be done with a dictionary. Let's say you have a "Status" enum with 3 values. Complete, Incomplete, and Abandoned. Then you'll want to create a dictionary to be used as a lookup table.
Dictionary<Status, string> statusDict = new Dictionary<Status, string>()
{
{ Status.Complete, "Complete" },
{ Status.Incomplete, "Incomplete" },
{ Status.Abandoned, "Abandoned" }
};
When you receive the Status enum from them (CustomerStatus), all you need to do for your API call is...
apicall(customerID, statusDict[CustomerStatus]);

Make an switch case that triggers based on Guid

I am working on a switch case that triggers based certain guid is encountered..
Problem is that I can not store the guid without making it static readonly.
How do i resolve this issue?
public struct Types
{
public static readonly Guid Standard = new Guid("{11111111-1111-1111-1111-111111111111}");
public static readonly Guid Morning = new Guid("{22222222-2222-2222-2222-222222222222}");
}
public string GetStyle(Guid stage)
{
switch (stage)
{
case Types.Standard:
return "Not normal";
case Types.Morning:
return "Not anormal";
default:
return "Normal";
break;
}
}
With the latest switch syntax (aka "pattern matching") you can achieve that:
public static string GetStyle(Guid stage)
{
switch (stage)
{
case Guid standard when standard == new Guid("{11111111-1111-1111-1111-111111111111}"):
return "Not normal";
case Guid morning when morning == new Guid("{22222222-2222-2222-2222-222222222222}"):
return "Not anormal";
default:
return "Normal";
}
}
One way to handle this is to use a Dictionary<Guid, string> to map the guids to their string counterparts, and then just return a value from the dictionary if it exist. This alleviates the need for a switch statement altogether, and should result in cleaner code.
private Dictionary<Guid, string> StyleMap = new Dictionary<Guid, string>
{
{Types.Standard, "Not normal" },
{Types.Morning, "Not anormal" },
};
public string GetStyle(Guid stage)
{
string result;
return StyleMap.TryGetValue(stage, out result) ? result : "Normal";
}

MVVM - Does validation really have to be so cumbersome?

In my application I have tons of forms, most of with having there own models which they bind to! Of course data validation is important, but is there not a better solution than implementing IDataErrorInfo for all of your models and then writing code for all of the properties to validate them?
I have created validation helpers which remove alot of the actual validation code, but still I can't help but feel I am missing a trick or two! Might I add that this is the first application which I have used MVVM within so I am sure I have alot to learn on this subject!
EDIT:
This is the code from a typical model that I really don't like (let me explain):
string IDataErrorInfo.Error
{
get
{
return null;
}
}
string IDataErrorInfo.this[string propertyName]
{
get
{
return GetValidationError(propertyName);
}
}
#endregion
#region Validation
string GetValidationError(String propertyName)
{
string error = null;
switch (propertyName)
{
case "carer_title":
error = ValidateCarerTitle();
break;
case "carer_forenames":
error = ValidateCarerForenames();
break;
case "carer_surname":
error = ValidateCarerSurname();
break;
case "carer_mobile_phone":
error = ValidateCarerMobile();
break;
case "carer_email":
error = ValidateCarerEmail();
break;
case "partner_title":
error = ValidatePartnerTitle();
break;
case "partner_forenames":
error = ValidatePartnerForenames();
break;
case "partner_surname":
error = ValidatePartnerSurname();
break;
case "partner_mobile_phone":
error = ValidatePartnerMobile();
break;
case "partner_email":
error = ValidatePartnerEmail();
break;
}
return error;
}
private string ValidateCarerTitle()
{
if (String.IsNullOrEmpty(carer_title))
{
return "Please enter the carer's title";
}
else
{
if (!ValidationHelpers.isLettersOnly(carer_title))
return "Only letters are valid";
}
return null;
}
private string ValidateCarerForenames()
{
if (String.IsNullOrEmpty(carer_forenames))
{
return "Please enter the carer's forename(s)";
}
else
{
if (!ValidationHelpers.isLettersSpacesHyphensOnly(carer_forenames))
return "Only letters, spaces and dashes are valid";
}
return null;
}
private string ValidateCarerSurname()
{
if (String.IsNullOrEmpty(carer_surname))
{
return "Please enter the carer's surname";
}
else
{
if (!ValidationHelpers.isLettersSpacesHyphensOnly(carer_surname))
return "Only letters, spaces and dashes are valid";
}
return null;
}
private string ValidateCarerMobile()
{
if (String.IsNullOrEmpty(carer_mobile_phone))
{
return "Please enter a valid mobile number";
}
else
{
if (!ValidationHelpers.isNumericWithSpaces(carer_mobile_phone))
return "Only numbers and spaces are valid";
}
return null;
}
private string ValidateCarerEmail()
{
if (String.IsNullOrWhiteSpace(carer_email))
{
return "Please enter a valid email address";
}
else
{
if (!ValidationHelpers.isEmailAddress(carer_email))
return "The email address entered is not valid";
}
return null;
}
private string ValidatePartnerTitle()
{
if (String.IsNullOrEmpty(partner_title))
{
return "Please enter the partner's title";
}
else
{
if (!ValidationHelpers.isLettersOnly(partner_title))
return "Only letters are valid";
}
return null;
}
private string ValidatePartnerForenames()
{
if (String.IsNullOrEmpty(partner_forenames))
{
return "Please enter the partner's forename(s)";
}
else
{
if (!ValidationHelpers.isLettersSpacesHyphensOnly(partner_forenames))
return "Only letters, spaces and dashes are valid";
}
return null;
}
private string ValidatePartnerSurname()
{
if (String.IsNullOrEmpty(partner_surname))
{
return "Please enter the partner's surname";
}
else
{
if (!ValidationHelpers.isLettersSpacesHyphensOnly(partner_surname))
return "Only letters, spaces and dashes are valid";
}
return null;
}
private string ValidatePartnerMobile()
{
if (String.IsNullOrEmpty(partner_mobile_phone))
{
return "Please enter a valid mobile number";
}
else
{
if (!ValidationHelpers.isNumericWithSpaces(partner_mobile_phone))
return "Only numbers and spaces are valid";
}
return null;
}
private string ValidatePartnerEmail()
{
if (String.IsNullOrWhiteSpace(partner_email))
{
return "Please enter a valid email address";
}
else
{
if (!ValidationHelpers.isEmailAddress(partner_email))
return "The email address entered is not valid";
}
return null;
}
#endregion
The idea of having a switch statement to identify the correct property and then having to write unique validation functions for each property just feels too much (not in terms of work to do, but in terms of the amount of code required). Maybe this is an elegant solution, but it just doesn't feel like one!
Note: I will be converting my validation helpers into extensions as recommended in one of the answers (thanks Sheridan)
SOLUTION:
So, following the answer which I have accepted this is the bare bones of what I implemented to get it working initially (obviously I will be improving parts - but I just wanted to get it going first as I had little experience using lambda expressions or reflection prior to implementing this).
Validtion Dictionary class (showing the main functions):
private Dictionary<string, _propertyValidators> _validators;
private delegate string _propertyValidators(Type valueType, object propertyValue);
public ValidationDictionary()
{
_validators = new Dictionary<string, _propertyValidators>();
}
public void Add<T>(Expression<Func<string>> property, params Func<T, string>[] args)
{
// Acquire the name of the property (which will be used as the key)
string propertyName = ((MemberExpression)(property.Body)).Member.Name;
_propertyValidators propertyValidators = (valueType, propertyValue) =>
{
string error = null;
T value = (T)propertyValue;
for (int i = 0; i < args.Count() && error == null; i++)
{
error = args[i].Invoke(value);
}
return error;
};
_validators.Add(propertyName, propertyValidators);
}
public Delegate GetValidator(string Key)
{
_propertyValidators propertyValidator = null;
_validators.TryGetValue(Key, out propertyValidator);
return propertyValidator;
}
Model implementation:
public FosterCarerModel()
{
_validationDictionary = new ValidationDictionary();
_validationDictionary.Add<string>( () => carer_title, IsRequired);
}
public string IsRequired(string value)
{
string error = null;
if(!String.IsNullOrEmpty(value))
{
error = "Validation Dictionary Is Working";
}
return error;
}
IDataErrorInfo implementation (which is part of the model implementation):
string IDataErrorInfo.this[string propertyName]
{
get
{
Delegate temp = _validationDictionary.GetValidator(propertyName);
if (temp != null)
{
string propertyValue = (string)this.GetType().GetProperty(propertyName).GetValue(this, null);
return (string)temp.DynamicInvoke(typeof(string), propertyValue);
}
return null;
}
}
Ignore my slapdash naming conventions and in places coding, I am just so pleased to have got this working! A special thanks to nmclean of course, but also thanks to everyone that contributed to this question, all of the replies were extremely helpful but after some consideration I decided to go with this approach!
I use extension methods to reduce the amount of validation text that I have to write. If you are unfamiliar with them, please take a look at the Extension Methods (C# Programming Guide) page at MSDN to find out about extension methods. I have dozens of these that validate every situation. As an example:
if (propertyName == "Title" && !Title.ValidateMaximumLength(255)) error =
propertyName.GetMaximumLengthError(255);
In the Validation.cs class:
public static bool ValidateMaximumLength(this string input, int characterCount)
{
return input.IsNullOrEmpty() ? true : input.Length <= characterCount;
}
public static string GetMaximumLengthError(this string input, int characterCount,
bool isInputAdjusted)
{
if (isInputAdjusted) return input.GetMaximumLengthError(characterCount);
string error = "The {0} field requires a value with a maximum of {1} in it.";
return string.Format(error, input, characterCount.Pluralize("character"));
}
Note that Pluralize is another extension method that simply adds an "s" to the end of the input parameter if the input value does not equal 1. Another method might be:
public static bool ValidateValueBetween(this int input, int minimumValue, int
maximumValue)
{
return input >= minimumValue && input <= maximumValue;
}
public static string GetValueBetweenError(this string input, int minimumValue, int
maximumValue)
{
string error = "The {0} field value must be between {1} and {2}.";
return string.Format(error, input.ToSpacedString().ToLower(), minimumValue,
maximumValue);
}
Of course, it will take a while to implement all the methods that you will require, but then you'll save plenty of time later and you will have the added benefit of all of your error messages being consistent.
I personally like the FluentValidation approach.
This replaces your switch table with expression based rules like:
RuleFor(x => x.Username)
.Length(3, 8)
.WithMessage("Must be between 3-8 characters.");
RuleFor(x => x.Password)
.Matches(#"^\w*(?=\w*\d)(?=\w*[a-z])(?=\w*[A-Z])\w*$")
.WithMessage("Must contain lower, upper and numeric chars.");
RuleFor(x => x.Email)
.EmailAddress()
.WithMessage("A valid email address is required.");
RuleFor(x => x.DateOfBirth)
.Must(BeAValidDateOfBirth)
.WithMessage("Must be within 100 years of today.");
from http://stevenhollidge.blogspot.co.uk/2012/04/silverlight-5-validation.html
There's more information on this http://fluentvalidation.codeplex.com/ - although the docs there are mainly web-MVC based. For Wpf, there are also a few blog posts around like http://blogsprajeesh.blogspot.co.uk/2009/11/fluent-validation-wpf-implementation.html
You're right. A switch statement is too much. It's much easier to isolate IDEI (and INotifyDataErrorInfo) logic into a base class.
A simple way to accomplish this is to expose a method to set an error on a property, and to clear a property's error. This would be simple to implement, although you would have to code the validation for each property.
public string SomeProperty { get { return _someProperty; }
set
{
_someProperty = value;
if(string.IsNullOrWhiteSpace(value))
SetError("SomeProperty", "You must enter a value or something kthx");
else
ClearError("SomeProperty");
}
Where, in the base class, you keep a Dictionary that simply holds these error values
protected void SetError(string propertyName, string error)
{
_errors[propertyName] = error;
{
and delivers them on demand, e.g.,
string IDataErrorInfo.Error
{
get
{
return string.Join(Environment.NewLine, _errors.Values);
}
}
This kind of pattern can become more powerful when you combine it with Data annotations, a little reflection, and some features from 4.5 to avoid validation completely.
There are several examples of using the CallerMemberNameAttribute to simply and cleanly implement INotifyPropertyChanged in a base class. If you have the name of the property being set, and use reflection (cache it after the first call if you're worried about perf) to get any data annotations on the property, you can perform all your validation checks and store the results all within the base class. That would simplify your derived class' properties to something like the following:
[NotNullOrWhiteSpace, NotADirtyWord, NotViagraSpam]
public string SomeProperty{
get {return _lol;}
set{ _lol = value; PropertyChanged(); } }
Which radically simplifies the whole validation pipeline for only a small amount of work.
Mine looks something like this:
new ValidationDictionary() {
{() => carer_title,
ValidationHelpers.Required(() => "Please enter the carer's title"),
ValidationHelpers.LettersOnly(() => "Only letters are valid")}
}
ValidationDictionary is a dictionary of string -> delegate. It overloads Add to accept a lambda expression which is converted to a property name string for the key, and a params array of delegates that are consolidated into one delegate for the value. The delegates accept some information like the property type and value, and return an error message or null.
In this case, Required and LettersOnly are higher-order functions which generate delegates that return the given strings when invalid. The strings themselves are passed in as delegates so they can be dynamic.
IDataErrorInfo is implemented by simply looking up the property name in the dictionary and calling the delegate to get an error message.

In C#, is it possible to call a method (which has default parameters) with "as many parameters as I have"?

That is, I have a method such as the following:
public static int CreateTaskGroup(string TaskGroupName,
string Market = "en-us", string Project = "MyProject",
string Team = "DefaultTeam", string SatelliteID="abc");
I would like to call this method from the command line, by reading the standard array of command line arguments. The obvious way to do it would be as follows:
if (args.Length == 1) CreateTaskGroup(args[0]);
if (args.Length == 2) CreateTaskGroup(args[0], args[1]);
if (args.Length == 3) CreateTaskGroup(args[0], args[1], args[2]);
Is it possible to do this in a more concise way?
Here's one alternative, with the downside that you have to redeclare the default value constants:
CreateTaskGroup(
args[0],
args.ElementAtOrDefault(1) ?? "en-us",
args.ElementAtOrDefault(2) ?? "MyProject",
args.ElementAtOrDefault(3) ?? "DefaultTeam",
args.ElementAtOrDefault(4) ?? "abc");
You can reduce this issue by declaring the strings as consts, e.g.:
public const string MarketDefault = "en-us";
public static int CreateTaskGroup(string TaskGroupName,
string Market = MarketDefault, ...)
static void Main(string[] args)
{
CreateTaskGroup(
args[0],
args.ElementAtOrDefault(1) ?? MarketDefault,
...);
}
But then it's not guaranteed by the compiler, nor overtly obvious, that MarketDefault is, in fact, still (code can be refactored in the future) the default for Market.
Edit: Here's an alternate solution, using reflection:
var argsForMethod = new List<string>(args);
var m = typeof(Program).GetMethod("CreateTaskGroup");
foreach (var p in m.GetParameters().Skip(args.Length))
if (p.Attributes.HasFlag(ParameterAttributes.HasDefault))
argsForMethod.Add((string)p.DefaultValue);
else
throw new NotImplementedException();
var result = (int)m.Invoke(null, argsForMethod.ToArray());
This can be a bit hard to read, and won't be too fast, but it does what you asked, without resorting to repetitive code, or having any uncertainty as to the default value of the parameters. You'll probably want to add some error handling for too few or too many parameters. I prefer this solution.
How about using params in CreateTaskGroup something like this
public static int CreateTaskGroup(params string[] args)
{
for ( int i = 0 ; i < args.Length ; i++ )
{
............
This is how I would implement the class to keep things clean and to assign the responsibility of knowing the default values to the TaskGroupCreator.
public class TaskGroupCreator
{
private string[] values;
public TaskGroupCreator(string[] values)
{
this.values = values;
}
public string TaskGroupName
{
get { return values[0]; }
}
public string Market
{
get { return this.GetElement(1, "en-us"); }
}
public string Project
{
get { return this.GetElement(2, "MyProject"); }
}
public string Team
{
get { return this.GetElement(3, "DefaultTeam"); }
}
public string SatelliteID
{
get { return this.GetElement(4, "abc"); }
}
public int CreateTaskGroup()
{
// Do stuff with your properties...
}
private string GetElement(int index, string defaultValue)
{
return this.values.ElementAtOrDefault(index) ?? defaultValue;
}
}
Usage:
var taskGroup = new TaskGroupCreator(args).CreateTaskGroup();
I'd do it this way..
CreateTaskGroup(args);
//.....
public static int CreateTaskGroup(params string[] args) {
if (args.Length == 0) throw new Exception("nope!");
args = args.Concat(Enumerable.Range(0, 5 - args.Length)
.Select<int, string>(_ => null)).ToArray();
string TaskGroupName = args[0];
string Market = args[1] ?? "en-us";
string Project = args[2] ?? "MyProject";
string Team = args[3] ?? "DefaultTeam";
string SatelliteID = args[4] ?? "abc";
//......
}
params keyword isn't mandatory, but could be convenient...
This is probably best out of what I've come up with:
public static int CreateTaskGroup(string[] arguments)
{
// optional error handling here
string TaskGroupName = arguments[0];
string Market = arguments.ElementAtOrDefault(1) ?? "en-us";
string Project = arguments.ElementAtOrDefault(2) ?? "MyProject";
string Team = arguments.ElementAtOrDefault(3) ?? "DefaultTeam";
string SatelliteID = arguments.ElementAtOrDefault(4) ?? "abc";
// function body as it was
This does the same thing but is less concise:
public static int CreateTaskGroup(string[] arguments)
{
string TaskGroupName, Market, Project, Team, SatelliteID;
switch (arguments.Length)
{
case 5:
string SatelliteID = arguments[4] ?? "abc";
goto case 4;
case 4:
string Team = arguments[3] ?? "DefaultTeam";
goto case 3;
case 3:
string Project = arguments[2] ?? "MyProject";
goto case 2;
case 2:
string Market = arguments[1] ?? "en-us";
goto case 1;
case 1:
string TaskGroupName = arguments[0];
break;
case 0:
// error handling here;
}
// function body as it was
You could call it concisely like this:
CreateTaskGroup(args);

Categories