I have 46 rows of information, 2 columns each row ("Code Number", "Description"). These codes are returned to the client dependent upon the success or failure of their initial submission request. I do not want to use a database file (csv, sqlite, etc) for the storage/access. The closest type that I can think of for how I want these codes to be shown to the client is the exception class. Correct me if I'm wrong, but from what I can tell enums do not allow strings, though this sort of structure seemed the better option initially based on how it works (e.g. 100 = "missing name in request").
Thinking about it, creating a class might be the best modus operandi. However I would appreciate more experienced advice or direction and input from those who might have been in a similar situation.
Currently this is what I have:
class ReturnCode
{
private int _code;
private string _message;
public ReturnCode(int code)
{
Code = code;
}
public int Code
{
get
{
return _code;
}
set
{
_code = value;
_message = RetrieveMessage(value);
}
}
public string Message { get { return _message; } }
private string RetrieveMessage(int value)
{
string message;
switch (value)
{
case 100:
message = "Request completed successfuly";
break;
case 201:
message = "Missing name in request.";
break;
default:
message = "Unexpected failure, please email for support";
break;
}
return message;
}
}
The best would be both a class and an enumeration. Then you can have more descriptive identifiers than "201".
A structure would also work, but they are harder to implement correctly, so you should stick to a class unless you specifically need a structure for some reason.
You don't need to store a reference to the message in the class, you can get that when needed in the Message property. A switch is implemented using a hash table (if there are five values or more), so the lookup is very fast.
public enum ReturnIdentifier {
Success = 100,
MissingName = 201;
}
public class ReturnCode {
public ReturnIdentifier Code { get; private set; }
public ReturnCode(ReturnIdentifier code) {
Code = code;
}
public string Message {
get {
switch (Code) {
case ReturnIdentifier.Success:
return "Request completed successfuly.";
case ReturnIdentifier.MissingName:
return "Missing name in request.";
default:
return "Unexpected failure, please email for support.";
}
}
}
}
Usage:
ReturnCode code = new ReturnCode(ReturnIdentifier.Success);
If you get an integer code from somewhere, you can still use it as the enumerator values correspond to the codes:
int error = 201;
ReturnCode code = new ReturnCode((ReturnIdentifier)error);
(If the integer code doesn't correspond to any of the identifiers in the enumeration, it's still perfectly valid to do the conversion. When getting the Message value, it will end up in the default case as the value doesn't match any of the other cases.)
I think choosing a class(as you did) is a good decision. You can make the code a little more compact and readable, if you use Dictionary<int, string> for mapping codes to descriptions.
_dict.Add(100, "Description1");
_dict.Add(201, "Description2");
...............................
And RetrieveMessage:
return _dict[value];
How about deriving from Dictionary, or storing the data table in code using a Dictionary field that you can index into?
Maybe a dictionary based approach would look more elegant.
private static Dictionary<int, string> errorCodes =
new Dictionary<int, string>()
{
{100, "Request completed successfuly"},
{200, "Missing name in request."}
};
private string RetrieveMessage(int value)
{
string message;
if (!errorCodes.TryGetValue(value, out message))
message = "Unexpected failure, please email for support";
return message;
}
It will definitely be more slower (since it uses Reflection) but speaking of being compact, I think Enums With Custom Attributes is appropriate for this need. Please continue reading the comments since DescriptionAttribute is mentioned there. Something like;
public enum ErrorMessage
{
[System.ComponentModel.Description("Request completed successfuly")]
Success = 100,
[System.ComponentModel.Description("Missing name in request.")]
MissingName = 201
};
public static string GetDescription(this Enum en)
{
Type type = en.GetType();
System.Reflection.MemberInfo[] memInfo = type.GetMember(en.ToString());
if (memInfo != null && memInfo.Length > 0)
{
object[] attrs = memInfo[0].GetCustomAttributes(typeof(System.ComponentModel.DescriptionAttribute),
false);
if (attrs != null && attrs.Length > 0)
return ((System.ComponentModel.DescriptionAttribute)attrs[0]).Description;
}
return en.ToString();
}
static void Main(string[] args)
{
ErrorMessage message = ErrorMessage.Success;
Console.WriteLine(message.GetDescription());
}
Related
I'm relatively new/inexperienced to c# and I am trying to write a bool method to validate if the user input in a windows form is empty, which returns as true or false, as well as change the errorMessage Variable to have new text if it returns false.
public static bool IsPresent(string value)
{
if (value == "")
{
errorMessage = "all textboxes and combo boxes must be filled";
return false;
}
else
{
errorMessage = "";
return true;
}
}
I get compile time error on errorMessage, saying
"An object reference is required for the nonstatic field, method, or property 'member'".
I declared the errorMessage variable at the top of my file and made it public.
I have tried getting rid of the static aspect of the method, which does fix it, but causes more errors elsewhere in my code.
Do you know how I can go about fixing this?
You can declare public static field and do it like this of course:
static class Helper
{
public static string errorMessage;
public static bool IsPresent(string value)
{
if (value == "")
{
errorMessage = "all textboxes and combo boxes must be filled";
return false;
}
else
{
errorMessage = "";
return true;
}
}
}
But this's got a few problems, one of them is that errorMessage can be now modified by some foreign class, so it should be changed to something else, for example:
private static string errorMessage;
public static string GetMessageCopy()
{
return errorMessage;
}
You can change this to property, which does the same thing, but it's easier to read:
public static string ErrorMessage { get; private set; } // property should always start with a big letter
Now only Helper class can modify ErrorMessage, but everyone can read it.
Next problem is that name of your parameter "value" is a C# keyword, which is used for different things, so please, think of renaming it.
You need to declare variable first inside the static function. If you have declared it outside the method's scope I recommend removing the static keyword so it should look like this:
string errorMessage;
bool IsPresent(string value)
{
if (value == "")
{
errorMessage = "all textboxes and combo boxes must be filled";
return false;
}
else
{
errorMessage = "all textboxes and combo boxes must be filled";
return true;
}
}
You should read up on static. You could start with static (C# Reference).
However, there are solutions that don't require a member variable (what you called at the top of my file) and avoid the static/non-static issue. For example, let's consider this solution:
(bool IsValid, string? ErrorMessage) IsPresent(string? s)
{
if (string.IsNullOrEmpty(s))
{
return(IsValid: false, ErrorMessage: "All textboxes and combo boxes must be filled");
}
else
{
return(IsValid: true, ErrorMessage: null);
}
}
Notes:
Please note I used string.IsNullOrEmpty rather than == "". You may want to use string.IsNullOrWhiteSpace to make the requirement even stronger.
You may need to change (bool IsValid, string? ErrorMessage) IsPresent(string? s) to (bool IsValid, string ErrorMessage) IsPresent(string s) if you're not using a fresh version of .NET.
If we think about it a bit more we can notice that IsValid is redundant. Checking if the error is return is enough:
string? CheckValid(string? s)
{
if (string.IsNullOrWhiteSpace(s))
{
return "All textboxes and combo boxes must be filled";
}
//if ( ... some other requirement )
//{
// return "Some error";
//}
return null;
}
When considering 'data' validation upon import of csv data using CsvHelper, I was hoping to put all of my 'data' validation code in the ClassMap using the .Validate method.
I am have been looking for good examples of how to use the .Validate method to ensure that the data conforms to my business rules and also send good error message back to the user when validation rules have been violated.
A very basic example is located in the 'Configuration/Class Maps/Validation code located in 'examples' section of the CsvHelper website located here: https://joshclose.github.io/CsvHelper/examples/configuration/class-maps/validation/
I am able to predictably able to make and catch errors that I have set up using the .Validate method. However, I have not determined a way to give adequate information to my user to fix issues when they are encountered. It appears to me that the FieldValidationException does not contain enough information to be useful. I have seen others asking this question but the other posts that I have seen do not have an adequate answer to the problem. Please see my code and comments below.
class Program
{
static void Main(string[] args)
{
try
{
// load comma separated data into string for processing
var s = new StringBuilder();
s.AppendLine("Id,Name");
s.AppendLine("1,one-e");
// Begin processing
using (var reader = new StringReader(s.ToString()))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
csv.Context.RegisterClassMap<FooMap>();
var contents = csv.GetRecords<Foo>().ToList();
Console.WriteLine($"Read file: length is : {contents.First().Id},{contents.First().Name},{contents.First().Date}");
}
}
catch (FieldValidationException fieldExc)
{
// this exception object does not appear to hold any information about the column that is in error. It does not seem to specify what the error entails.
// It appears to contain the value that created the exception? In a 'string' field called: "Field".
// I would really like further information about what is specifically is wrong with which column and possibly which row.
Console.WriteLine($"Error Message is : {fieldExc.Message}");
}
catch(TypeConverterException converterExc)
{
// If I happen to have a conversion exception, then the resulting TypeConversionException has good information to advise
// a user to correct an error.
var message = FriendlyErrorText(converterExc);
Console.WriteLine(message);
}
catch (Exception exc)
{
Console.WriteLine($"Error Message is : {exc.Message}");
}
finally
{
Console.WriteLine("test executed");
Console.ReadKey();
}
}
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public DateTimeOffset? Date { get; set; }
}
public class FooMap : ClassMap<Foo>
{
public FooMap()
{
Map(m => m.Id);
Map(m => m.Name).Validate(field => !field.Field.Contains("-"));
}
}
public static string FriendlyErrorText(TypeConverterException exception)
{
var column = exception?.MemberMapData?.Member?.Name ?? "Unknown";
string typeConversion;
switch (exception?.TypeConverter?.ToString())
{
case "Int32Converter":
typeConversion = "integer";
break;
default:
typeConversion = exception?.TypeConverter?.GetType()?.Name ?? "Unknown";
break;
}
var message = $"There was an error importing the text data '{exception?.Text ?? "Unknown"}' into the column {column}. The target column is of type {typeConversion}";
return message;
}
}
FieldValidationException includes the CsvContext, which has most of what you would need.
catch (FieldValidationException fieldExc)
{
var message = string.Empty;
var headerIndex = fieldExc.Context.Reader.CurrentIndex;
if(fieldExc.Context.Reader.HeaderRecord[headerIndex].ToLower() == "name")
{
message = "Name cannot contain a dash.";
}
Console.WriteLine($"Error Message is : Field Validation Exception on Row: {fieldExc.Context.Parser.Row}, Message: {message} Original Text: \"{fieldExc.Field}\"");
}
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);
I have been writing a check in a name property of my person abstract class. The problem that i have is that i am trying to implement a piece of code that will not allow the user to leave the field empty or to exceed the name limit with 35characters or in-put a digit but i am stuck with it. If any one can help or suggest me.
public string Name
{
get { return name; }
set
{
while (true)
{
if (value == "" || value.Length > 35)
{
Console.Write("Please Enter Correct Name: ");
value = Console.ReadLine();
continue;
}
foreach (char item in value)
{
if (char.IsDigit(item))
{
Console.Write("Digits Are NotAllowed....\n");
Console.Write("Please Enter Correct Name: ");
value = Console.ReadLine();
break;
}
}
break;
}
name = value;
}
}
Don't do any form of UI or I/O in a property.
public string Name
{
get { return _name; }
set
{
if (! Regex.IsMatch(value, #"\w{1-35}"))
throw new ArgumentException("Name must be 1-35 alfanum");
_name = value;
}
}
The exact regular expression is up for discussion but the best practice:
do not try to list and reject all the patterns you don't like. Too much possibilities.
accept what you expect (and understand), reject everything else.
This sort of validation should be broken up. The setter should only know the various restrictions that it has and throw an exception in the case that an invalid value makes it that far. Do not put user interface code in there.
Try something like this:
public string Name
{
get { return name; }
set
{
if (value == "" || value.Length > 35)
{
throw new ArgumentException("Invalid name length.");
}
foreach (char item in value)
{
if (char.IsDigit(item))
{
throw new ArgumentException("Digits are not allowed.");
}
}
name = value;
}
}
Then something like this in your console application:
bool success = false;
while(!success)
{
try
{
Console.WriteLine("Please enter a name:");
myObject.Name = Console.ReadLine();
success = true;
}
catch(ArgumentException ex)
{
Console.WriteLine(ex.Message);
}
}
First of all, never ask for Console input inside of a setter. It is a seriously bad practice. Instead, you should throw an Exception from the setter and let the caller handle that however they need:
public string Name
{
get { return name; }
set
{
if(String.IsNullOrWhiteSpace(value))
throw new ArgumentException("Name must have a value");
if(value.Length > 35)
throw new ArgumentException("Name cannot be longer than 35 characters");
if(value.Any(c => char.IsDigit(c))
throw new ArgumentException("Name cannot contain numbers");
name = value;
}
}
You can then catch and handle the Exceptions appropriately in the calling code (which, in your case, would involve re-prompting the user for the input).
The solution for handling this according to your rules are almost obvious but the thing is, it's better not to put the checking and validating logic in the setter method of a property, you can have a separate class for instance and that class does the validation responsibility for you and you can tell it to do that and then use the result appropriately. In that case you are following "Tell, Don't Ask" rule and also "Single Responsibility Principle"
Good Luck
public string Name
{
get { return name; }
set { name = value; }
}
public static bool IsNameValid(string name)
{
if (string.IsNullOrEmpty(name) || name.Length > 35)
{
return false;
}
foreach (char item in value)
{
if (!char.IsLetter(item))
{
return false;
}
}
return true;
}
Finally a code snippet for reading an user input.
var yourClassInstance = new YourClass();
string input
bool inputRead = false;
while(!inputRead)
{
var input = Console.ReadLine();
inputRead = YourClass.IsNameValid(input);
}
yourClassInstance.Name = inputRead;
The short answer for this is to loop while the value is not valid:
public string GetName()
{
String name = String.Null;
do
{
Console.Write("Please Enter Correct Name: ");
name = Console.ReadLine();
} while (!ValidateName(name))
}
public bool ValidateName(string name)
{
//String validation routine
}
That being said, as I'm sure you will see from other answers, change where the Name is given. As a general rule, accessors are really just for "getting" and "setting" quickly what's in a class.
I would create a method for changing the name that contains the validation logic. If you want to check the name is valid, so you don't have to handle the argumentexception do a check first, call IsValidName before calling ChangeName
public class Person
{
public void ChangeName(string name)
{
if (!IsValidName(name))
{
throw new ArgumentException(....);
}
else
this.Name = value;
}
public bool IsValidName(string name)
{
// is the name valid using
}
public string Name { get; private set; }
}
And to use it
var newName = Console.ReadLine();
var person = new Person();
while (!person.IsValidName(newName))
{
newName = Console.ReadLine();
}
person.ChangeName(newName);
From a semantics point of view, a setter is as its name says, a setter! It should be used to set a private/protected field of a class
From a testability point of view, your design is very hard to be automatically tested not to say impossible!
This reminds me of a bit of code I worked on sometime ago where a setter is opening a socket and sending stuff over the network!
The code should do what it reads, just imagine if someone uses your code, calls your setter and wonders why on earth does his/her application hang (waiting for user input)
The way I see your code more readable and testable is to have a verifer class that ensures the user is entering the right data in the right format. The verifier should take an input stream as data source, this will help you easily test it.
Regards,
Aside from what Mr Skeet said, seems like you should replace this break with a continue in order to validate the new value (like you do in your first length check):
if (char.IsDigit(item))
{
Console.Write("Digits Are NotAllowed....\n");
Console.Write("Please Enter Correct Name: ");
value = Console.ReadLine();
continue; //here
}
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.