Checking for incorrect input inside properties in C# - c#

I am using properties to allow a single string name to be added to the class Details, I want the property to only accept the string if it can be split into two parts.
The two parts would be the firstName and the LastName. However if the resulting split has 1,0 or more than 2 strings in the array then the input should be deemed invalid and I want to throw a error to whatever code called the property in the first place.
Can error handling be done on properties like this?
If not then what of the following is the preferred way to get data into a class whilst checking for correctness:
Use a method inside the class Details to handle error inputs, make that method boolean.
Continue using properties but have the error checking done by the code that calls the property. I don't like this because I want all the error checking code to be self-contained in the Details class.
.
class Details
{
private string firstName, lastName;
public string Name
{
// name
get { return firstName + " " + lastName; }
set
{
string name = value;
string[] nameArray = name.Split(' ');
firstName = nameArray[0];
lastName = nameArray[1];
}
}
}
EDIT: I am mostly interested in what of the three options is concidered best pratice:
Error check inside properties.
Error check outside class in another class, and then just add the verified inputs to Details
Use a boolean method inside Details to verify the inputs.

I would follow an existing validation framework, such as FluentValidation.
Also, in your specific case, I would have a SetName(string fullName) method that does the parsing and populating.

Why not use exception to capture the error condition.
private string firstName, lastName;
public string Name
{
get { return string.Concat(firstName, " ", lastName); }
set
{
string name = value;
if (string.IsNullOrEmpty(name)) { throw new ArgumentNullException("Name"); }
var nameParts = name.Trim().Split(' ');
if (nameParts.Length != 2) { throw new ArgumentException("Invalid name value"); }
firstName = nameParts[0];
lastName = nameParts[1];
}
}

Just to add my perspective to what others have said, it seems to me that part of the difficulty stems from the fact that you're using a property setter to do something non-trivial (split a string, validate the results, and then store the results in two fields). Generally, properties, especially read/write properties, should only be concerned with getting and setting a single value (using read-only properties to return a simple computed value is common enough too).
If it were me, I'd have separate properties for the first name and last name, with a third computed property for the full name:
class Details
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get { return string.Concat(this.FirstName, " ", this.LastName); }
}
}
Then you could add a method to set the full name. A method is more appropriate than a property setter for this task, since it's non-trivial and has more potential for problems:
public void SetFullName(string fullName)
{
string[] nameComponents = fullName.split(' ');
if (nameComponents.Length != 2)
{
throw new ArgumentException("The full name must contain a first and last name.");
}
this.FirstName = nameComponents[0];
this.LastName = nameComponents[1];
}
I also want to give a plug for the Code Contracts package. It may be more complication than you're looking for here, but it's a great way of validating input and output in your applications.

So I wouldn't consider data that doesn't meet business logic an exception and thus wouldn't throw one. What I would do is this:
class Details
{
private string firstName, lastName;
public string Name
{
// name
get { return firstName + " " + lastName; }
set
{
string name = value;
string[] nameArray = name.Split(' ');
if(nameArray.Length == 2)
{
firstName = nameArray[0];
lastName = nameArray[1];
}
else
{
firstName = nameArray[0];
lastName = string.Empty;
}
}
}
public bool IsValid()
{
return !string.IsNullOrEmpty(lastName);
}
}
You can then use the name property and then check to see if the name is valid. If not valid, then you can take the appropriate action.
Another option would be to have the validation done in the method calling Details.Name.
EDIT: want to remove what I think is bad advice but keeping so comments make sense, so just striking them out
EDIT2:
You could also do something like this:
class Details
{
private string firstName, lastName;
public string Name
{
get { return firstName + " " + lastName; }
private set;
}
public bool TryParseName(string name)
{
bool isValid = true;
string[] nameParts = name.split(' ');
if(nameParts.Length == 2)
{
firstName = nameParts[0];
lastName = nameParts[1];
}
else
{
isValid = false;
}
return isValid;
}
}
Where you would do
if(details.TryParseName(name))
{
// is valid name
}
else
{
// handle invalid name
}

Related

FluentValidation: Update the validated model for some optional properties inside the validator

I was wondering if there is possible to update the validated model inside the FluentValidations validator for some optional parameters, in case that these parameters are invalid?
Here is some code:
public class Customer
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
}
public class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.FirstName).NotNull().Length(5, 100);
RuleFor(customer => customer.LastName).NotNull().Length(5, 100);
RuleFor(customer => customer.MiddleName).Length(5, 100).When(c => string.IsNullOrWhiteSpace(c.MiddleName));
}
}
And I'm using it like this:
Customer customer = new Customer { FirstName = "first name", LastName = "last name" };
CustomerValidator validator = new CustomerValidator();
var result = validator.Validate(customer);
Console.WriteLine(result.IsValid);
So in my case, I want to set the middle name to null when it's not valid and save a warning in a variable but still treat the model as valid.
Actually manipulating the passed-in value is more than just validation. So FluentValidator might not be the best place for it. How about putting that particular logic in the setter?:
string middleName;
public string MiddleName {
get => middleName;
set {
middleName =
value.Length < 5 || value.Length > 100
? null
: value;
}
Edit: Responding to concerns raised in comments
So, regarding your desire to show a warning message. I assume you already know how to do this within FluentValidation. It seems like a not-so-fun topic, at least when I looked up this resource. So I'll leave the actual warning logic out of this response.
But, in order to give you a fighting chance at even raising such a warning, you need it stored somewhere. So just add a field for this and handle it in your setter logic:
public string middleNameWarning;
string middleName;
public string MiddleName {
get => middleName;
set {
if (value.Length < 5 || value.Length > 100) {
middleName = null;
middleNameWarning =
$"'{value}' is not a valid MiddleName. It was reset to null. "
+ "Set MiddleName explicitly to a valid value to remove this warning.";
}
else {
middleName = value;
middleNameWarning = null;
}
}
}
middleNameWarning has to be public in order for you to access it (I was able to confirm this) with FluentValidation. If you don't like that maybe you can have a method access it. Also, it can possibly just be a boolean and you can set the text elsewhere when you discover its value is true.
Finally, just to confirm that what your original approach might not work out as you hope, I discovered a closed issue on the GitHub repository for someone asking how to do something similar. Here was the response from JeremySkinner:
Hi, FluentValidation only performs validation on a pre-populated object, it doesn't modify/change the values of properties. You could technically do this with a custom validator that does the work, but I wouldn't really recommend it.
It is not a good idea to update the object passed to validation the Validator should only validate the input entity, and return the validation result and that is it. I would suggest you to have two validators. One for business critical rules and one for warnings so if the first validator returns that model is invalid you return the result, if no critical errors then you validate the warnings and then you can decide to proceed or not and manipulate with your model!
For example
public class Customer
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
}
public class CustomerCriticalValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.FirstName).NotNull().Length(5, 100);
RuleFor(customer => customer.LastName).NotNull().Length(5, 100);
}
}
public class CustomerWarningValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.MiddleName).Length(5, 100).When(c => string.IsNullOrWhiteSpace(c.MiddleName));
}
}
and then in the code
Customer customer = new Customer();
CustomerCriticalValidator criticalValidator = new CustomerCriticalValidator();
CustomerWarningValidator warningValidator = new CustomerWarningValidator();
var validationResult = criticalValidator.Validate(customer);
if (validationResult.IsValid)
{
var result = warningValidator.Validate(customer);
if (!result.IsValid)
{
//DO what you need with customer
}
}
I've managed to solve it:
public class CustomerValidator : AbstractValidator<Customer>
{
public CustomerValidator()
{
RuleFor(customer => customer.FirstName).NotNull().Length(5, 100);
RuleFor(customer => customer.LastName).NotNull().Length(5, 100).OnAnyFailure((customer) =>
{
customer.LastName = null;
customer.Warnings.Add(nameof(customer.LastName));
});
RuleFor(customer => customer.MiddleName).Length(5, 100).When(c => string.IsNullOrWhiteSpace(c.MiddleName));
}
}

Multi-level validation for an Object in C#

I have a Console project reads inputs from CSV file and tries to save them to database.
For that, I created a class Person that maps a CSV row.
The CSV file has two columns Name and Age. Person class is like.
class Person
{
public string Name;
public int Age;
}
So the list of all populated objects is List<Person>.
I have a new requirement to display validation messages to console before proceed with saving populated objects to database.
Validation has two levels: Error and Warning.
For example if Name property contains a special character, I have to display this message: "Error: Name contains special character"
In case Name properly contains a numeric character, I have to display only warning message: "Warning: Name contains numeric character"
I was thinking about using DataAnnotation but I cannot see a way to add different levels (Error and Warning) to validation process. Also, I'm not sure if DataAnnotation fits only in Web Applications.
Is there a way to add some functionality to Person class to get this validation done for each property?
NB: This is just an example to better understand the question, I have other rules for other properties.
You could change the Name field to a property, and incorporate this error checking into its setter. For checking if the string contains special characters though, you will need to build an array of characters you want to look for and see if any of those match a character within the string.
An example can be found here.. Special Character Check
class Person
{
private static readonly char[] numbers = "0123456789".ToCharArray();
private static readonly char[] specialChars = "!##$%^&*()".ToCharArray();
private string _name;
public string Name
{
set
{
if (Validate(value, "Name"))
_name = value;
}
get { return _name; }
}
public int age;
private bool Validate(string input, string varName)
{
bool isValid = true;
if (input.IndexOfAny(specialChars) != -1)
{
Console.WriteLine("Error- " + varName + " contains a special character.");
isValid = false;
}
if (input.IndexOfAny(numbers) != -1)
Console.WriteLine("Warning- " + varName + " contains a number.");
//optionally set isValid = false if warnings warrant this
return isValid;
}
}
Any other strings your person class contains can be formatted with properties in this same way.
add a public "validation" function inside Person - you will be able to call that function from outside the class
Use Regex class to search for digits.
define your own special characters inside an array and use Contains() method to check if the name contains special chars.
return aftermath using enum (one way to do that) and handle names that are not valid
here is a general idea for what you need (please see comments):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
Foo();
}
private static void Foo()
{
// init new object
Person p1 = new Person { age = 55, Name = "jonat8han" };
Person p2 = new Person { age = 55, Name = "jonat#han" };
Person p3 = new Person { age = 55, Name = "jonathan" };
List<Person> p = new List<Person>();
p.Add(p1);
p.Add(p2);
p.Add(p3);
foreach (Person person in p)
{
if (person.IsValid() == Person.Validation.IsWarning)
{
Console.WriteLine(person.Name + " has digit...");
Console.ReadLine();
// write here some logic to do somthing with this....
}
else if (person.IsValid() == Person.Validation.IsError)
{
Console.WriteLine(person.Name + " special char...");
Console.ReadLine();
// write here some logic to do somthing with this....
}
else if (person.IsValid() == Person.Validation.IsErrorAndWarning)
{
Console.WriteLine(person.Name + " special char and digit...");
Console.ReadLine();
// write here some logic to do somthing with this....
}
else
{
// everything IsOk
}
}
}
class Person
{
public enum Validation
{
IsWarning = 0,
IsError = 1,
IsErrorAndWarning = 2,
IsOk = 3
}
public string Name;
public int age;
public Validation IsValid()
{
if (IsError() && IsWarning())
{
return Validation.IsErrorAndWarning;
}
else if (IsError())
{
return Validation.IsError;
}
else if (IsWarning())
{
return Validation.IsWarning;
}
else
{
return Validation.IsOk;
}
}
private bool IsWarning()
{
// check if there are digits...
Regex reg = new Regex("\\d");
if (reg.IsMatch(this.Name) == true)
{
// there is digit
return true;
}
else
{
return false;
}
}
private bool IsError()
{
string[] speacialChars = new string[] { "*", "&", ".", "^", "#", "#" }; // define here what is a special character for your needs
foreach (Char c in this.Name)
{
if (speacialChars.Contains(c.ToString()))
{
return true;
}
}
return false;
}
}
}
}

Using a Class as ValueMember in ComboBox

I have a ComboBox, which currently uses a simple class, containing Name and ID. Name is used as DisplayMember whereas ID is used as ValueMember. However, I would actually like to pass both the Name and the ID, when selecting an item, since this would spare me the operation of looking up the name later. Of course I could store those seperately, but that seems rendundat, since they come from the same place.
Hence arises my question: Is it possible to use the class (from which I get the Name and ID) as ValueMember for the ComboBox?
I was thinking something like this:
cboCategory.DataSource = viewModel.categoryOptions; // Type: BindingList<Equipment>
cboCategory.DisplayMember = "Name";
cboCategory.ValueMember = ??? // <--- This is where I run out of ideas
My Equipment class looks like this:
public class Equipment
{
private int id;
private string name;
public Equipment (int id, string name)
{
this.id = id;
this.name = name;
}
public int Id
{
get { return id; }
set { id = value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
}
You can access selected instance with SelectedItem property of combobox.
Only you need is cast to Eqipment type before using because SelectedItem is of type object.
var selectedEquipment = (Equipment)combobox.SelectedItem;
You can use data-binding as well to keep your viewmodel "loosely coupled"
cboCategory.DataSource = viewModel.categoryOptions;
cboCategory.DisplayMember = "Name";
cboCategory.ValueMember = "Id";
cboCategory.DataBinding.Add("SelectedItem", viewModel, "SelectedEquipment", true);
With data-binding viewmodel.SelectedEquipment property will be updated when you change selected item in combobox.
There's no way how you can achieve this with pure C# without adding third property where you combine Name and ID.
You can consider that 3rd property security like:
Is it enough to have only get?
Is it enough to have it protected?
etc.
When you're using XAML or WinForms, there's MultiBinding mechanism to achieve similar behavior. IMHO, multi-binding is in most cases overhead and it is more beneficial to create 3rd property.
So your class would look like:
public class Equipment
{
private int id;
private string name;
public Equipment (int id, string name)
{
this.id = id;
this.name = name;
}
public int Id
{
get { return id; }
set { id = value; }
}
public string Name
{
get { return name; }
set { name = value; }
}
public string Identifier
{
get { return Id.ToString() + " " + Name; }
}
}
You can extent you ViewModel with INotifyPropertyChanged and notify about Identifier change when Name or ID changes.
More sophisticated (if needed) will be returning array of objects instead of string so you wont lose data at conversion (ID.ToString()) <- require more memory.

Read values from a non-delimited string into class object

I have a string with the following structure:
Student Name________AgeAddress_______________________Bithday___Lvl
Example:
Jonh Smith 016Some place in NY, USA 01/01/2014L01
As you can see, there is no delimited character like | or ,
Also, there is no space between fields (if you check, there is no space between Age/Address and Birthday/Level.
The size of each field is static so if data's length is less then it will contains white spaces.
I have a class that need to be filled with that information:
public class StudentData
{
public char[] _name = new char[20];
public string name;
public char[] _age = new char[3];
public string age;
public char[] _address = new char[30];
public string address;
public char[] _bday = new char[10];
public string bday;
public char[] _level = new char[3];
public string level;
}
Is there any way to do this automatically and dynamically?
I mean I really don't want to code like this:
myClass.name = stringLine.substring(0,19);
myClass.age = stringLine.substring(20,22);
That's because I have way more fields that the ones added in this example & way more string lines with other different data.
Update: There were supposed to be a lot of spaces between "Smith" and "016", but I don't know how to edit it.
Update2: If I use StringReader.Read() I can evade to use substring and indexes, but it isn't still so dynamically because I would need to repeat those 3 lines for each field.
StringReader reader = new StringReader(stringLine);
reader.Read(myClass._name, 0 myClass._name.Length);
myClass.name = new string(myClass._name);
Given your requirement I came up with an interesting solution. All be-it it may be more complex and longer than using the String.SubString() method as stated.
However this solution is transferable to other types and other string. I used a concept of Attributes, Properties, and Reflection to parse a string by a Fixed Length and setting the class Properties.
Note I did change your StudentData class to follow a more conventional coding style. Following this handy guide on MSDN: http://msdn.microsoft.com/en-us/library/xzf533w0(v=vs.71).aspx
Here is the new StudentData class. Note it uses the properties as opposed to fields. (Not discussed here).
public class StudentData
{
string name;
string age;
string address;
string bday;
string level;
[FixedLengthDelimeter(0, 20)]
public string Name { get { return this.name; } set { this.name = value; } }
[FixedLengthDelimeter(1, 3)]
public string Age { get { return this.age; } set { this.age = value; } }
[FixedLengthDelimeter(2, 30)]
public string Address { get { return this.address; } set { this.address = value; } }
[FixedLengthDelimeter(3, 10)]
public string BDay { get { return this.bday; } set { this.bday = value; } }
[FixedLengthDelimeter(4, 3)]
public string Level { get { return this.level; } set { this.level = value; } }
}
Note on each of the properties there is an Attribute called FixedLengthDelimeter that takes two parameters.
OrderNumber
FixedLength
The OrderNumber parameter denotes the order in the string (not the position) but the order in which we process from the string. The second parameter denotes the Length of the string when parsing the string. Here is the full attribute class.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public sealed class FixedLengthDelimeterAttribute : Attribute
{
public FixedLengthDelimeterAttribute(int orderNumber, int fixedLength)
{
this.fixedLength = fixedLength;
this.orderNumber = orderNumber;
}
readonly int fixedLength;
readonly int orderNumber;
public int FixedLength { get { return this.fixedLength; } }
public int OrderNumber { get { return this.orderNumber; } }
}
Now the attribute is simple enough. Accepts the two paramters we discussed eariler in the constructor.
Finally there is another method to parse the string into the object type such as.
public static class FixedLengthFormatter
{
public static T ParseString<T>(string inputString)
{
Type tType = typeof(T);
var properties = tType.GetProperties(BindingFlags.Instance | BindingFlags.Public); //;.Where(x => x.GetCustomAttributes(typeof(FixedLengthDelimeterAttribute), false).Count() > 0);
T newT = (T)Activator.CreateInstance(tType);
Dictionary<PropertyInfo, FixedLengthDelimeterAttribute> dictionary = new Dictionary<PropertyInfo, FixedLengthDelimeterAttribute>();
foreach (var property in properties)
{
var atts = property.GetCustomAttributes(typeof(FixedLengthDelimeterAttribute), false);
if (atts.Length == 0)
continue;
dictionary[property] = atts[0] as FixedLengthDelimeterAttribute;
}
foreach (var kvp in dictionary.OrderBy(x => x.Value.OrderNumber))
{
int length = kvp.Value.FixedLength;
if (inputString.Length < length)
throw new Exception("error on attribute order number:" + kvp.Value.OrderNumber + " the string is too short.");
string piece = inputString.Substring(0, length);
inputString = inputString.Substring(length);
kvp.Key.SetValue(newT, piece.Trim(), null);
}
return newT;
}
}
The method above is what does the string parsing. It is a pretty basic utility that reads all the properties that have the FixedLengthDelimeter attribute applied a Dictionary. That dictionary is then enumerated (ordered by OrderNumber) and then calling the SubString() method twice on the input string.
The first substring is to parse the next Token while the second substring resets the inputString to start processing the next token.
Finally as it is parsing the string it is then applying the parsed string to the property of the class Type provided to the method.
Now this can be used simply like this:
string data1 = "Jonh Smith 016Some place in NY, USA 01/01/2014L01";
StudentData student = FixedLengthFormatter.ParseString<StudentData>(data1);
What this does:
Parses a string against property attributes in a fixed length format.
What this does not do:
It does convert the parsed strings to another type. Therefore all the properties must be a string. (this can be easily adapted by adding some type casting logic in).
It is not well tested. This is only tested against a few samples.
It is not by all means the only or best solution out there.
You could use FileHelpers library (NuGet).
Just define the structure of your input file with attributes:
[FixedLengthRecord]
public class StudentData
{
[FieldFixedLength(20)]
[FieldTrim(TrimMode.Right)]
public string name;
[FieldFixedLength(3)]
public string age;
[FieldFixedLength(30)]
[FieldTrim(TrimMode.Right)]
public string address;
[FieldFixedLength(10)]
public string bday;
[FieldFixedLength(3)]
public string level;
}
Then simply read the file using FileHelperEngine<T>:
var engine = new FileHelperEngine<StudentData>();
var students = engine.ReadFile(filename);

Basic user-input string validation

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
}

Categories

Resources