So, i have a method
public void AddToSearch(List<FullName> fullNames)
{
foreach (var fullName in fullNames)
{
if (fullName.Surname != null)
_sb.Append(fullName.Surname.Trim() + " ");
if (fullName.Name != null)
_sb.Append(fullName.Name.Trim() + " ");
if (fullName.Patronymic != null)
_sb.Append(fullName.Patronymic.Trim());
fullNamesList.Add(_sb.ToString().TrimEnd());
_sb.Clear();
}
it takes a list of FullName and by using StringBuilder instance converts each element into a string(which format is "$Surname $Name $Patronymic"). At the end i put the result into my list. The Question is - how can i optimize all of that "Trim" stuff. It bothers me that i use it in multiple occassions and i am pretty sure it effects the time.
how can i optimize all of that "Trim" stuff
Very simple, simply don't call Trim() on those strings. What spaces are you worried about? Who's entering those values in your business objects? Because short of solar flares randomly flipping bits enough to append spaces to your strings, you're in full control from beginning to end, so simply don't add the spaces.
You also don't need the two string builders, just insert in your main one. There's no need for yet another Trim() here either, because simply decrementing the Length property of your string builder is a constant operation (it literally decrements one integer with guaranteed no extra allocations).
the strings normalization process should be done in the data layer (in application or database) for stored strings. While dynamic strings such as user input, needs to be normalized as soon as you get them to prepare them for the next task.
For your current code, you can modify the FullName class, adjust the setters to trim the value before it's been stored, and override the ToString to return the full name.
Example :
public class FullName
{
public string Name
{
get => Name;
set => Name = value?.Trim();
}
public string Surname
{
get => Surname;
set => Surname = value?.Trim();
}
public string Patronymic
{
get => Patronymic;
set => Patronymic = value?.Trim();
}
public override string ToString()
{
return $"{GetValueOrEmpty(Surname)}{GetValueOrEmpty(Name)}{GetValueOrEmpty(Patronymic, false)}";
}
private string GetValueOrEmpty(string name, bool addSpaceAfter = true)
{
if(!string.IsNullOrWhiteSpace(name))
{
return name + (addSpaceAfter ? " " : string.Empty);
}
return string.Empty;
}
}
Then, you can do this :
fullNamesList.AddRange(fullNames.Select(x=> x.ToString()));
UPDATE :
Thanks to #olivier-jacot-descombes, the above code is missing the use of backing fields, which will avoid causing overflow exception by the properties infinite recursions. The following adjustments will do the trick.
public class FullName
{
private string _name;
private string _surname;
private string _patronymic;
public string Name
{
get => _name;
set => _name = value?.Trim();
}
public string Surname
{
get => _surname;
set => _surname = value?.Trim();
}
public string Patronymic
{
get => _patronymic;
set => _patronymic = value?.Trim();
}
public override string ToString()
{
return $"{GetValueOrEmpty(Surname)}{GetValueOrEmpty(Name)}{GetValueOrEmpty(Patronymic, false)}";
}
private string GetValueOrEmpty(string name, bool addSpaceAfter = true)
{
if(!string.IsNullOrWhiteSpace(name))
{
return name + (addSpaceAfter ? " " : string.Empty);
}
return string.Empty;
}
}
Try and extension something like this.
public static class Helper
{
public static StringBuilder AppendValue(this StringBuilder builder,string value)
{
if(!string.IsNullOrEmpty(value))
{
builder.Append(value.Trim());
return builder;
}
}
}
call as follows:
sb.AppendValue(fullName.Name);
sb.AppendValue(fullName.Surname);
...
You will get the StringBuilder back with the value if it is not empty otherwise nothing will be added to it.
Related
I have a class say ConstructionSet which is having name property having computed inside get method like as below
public class ConstructionSet
{
[Key]
public Guid Id { get; set; }
public string Name
{
get
{
string climateZonesCsv = (ClimateZones != null && ClimateZones.Any())
? $" - {string.Join(", ", ClimateZones.Select(a => a.Name))}"
: "";
var sourceOfData = !string.IsNullOrEmpty(SourceOfData?.Edition)
? $"{SourceOfData.Name}, {SourceOfData.Edition}"
: SourceOfData?.Name;
return $"{sourceOfData} - {ConstructionMassingType?.Name}{climateZonesCsv}";
}
set { }
}
.......
.......
}
and then I am doing some operations like cloning this ConstructionSet object and appending some text like "copy" to the name property of ConstructionSet after deepclone and it is looks like as below
var targetDhpConstructionSets = sourceDhpConstructionSets.ConvertAll(
dhpcs =>
{
var newDhpConstructionSet = new DesignHubProjectConstructionSet(dhpcs);
var clone = DeepCloner.Clone(dhpcs.SectionObjectRaw);
clone.Name += " (Copy)";
newDhpConstructionSet.AddPatch(employeeContext, clone);
newDhpConstructionSet.IsDefault = dhpcs.IsDefault;
newDhpConstructionSet.Warnings = dhpcs.Warnings;
return newDhpConstructionSet;
});
and below is the image where it shows dhpcs.SectionObjectRaw is indeed a ConstructionSet class object and if i observe the clone object name property value after this line clone.Name += " (Copy)"; the Copy text is not appended.
Could any one please let me know why i am not seeing the appended values to the name property of ConstructionSet
many thanks in advance
because your setter does literally nothing:
set { }
Since your getter obtains several pieces of information on-the-fly, it's not obvious how to change your design to add on arbitrary text to what the getter provides. You could have a "suffix" property that gets appended to the strings that are currently returned, for example. Or have a backing field that the getter populates if it is null, then have the setter change the value of the backing field.
Based on your comments I would probably go with a backing field:
private string _Name;
public string Name
{
get
{
if(this._Name == null) {
string climateZonesCsv = (ClimateZones != null && ClimateZones.Any())
? $" - {string.Join(", ", ClimateZones.Select(a => a.Name))}"
: "";
var sourceOfData = !string.IsNullOrEmpty(SourceOfData?.Edition)
? $"{SourceOfData.Name}, {SourceOfData.Edition}"
: SourceOfData?.Name;
_Name = $"{sourceOfData} - {ConstructionMassingType?.Name}{climateZonesCsv}";
}
return _Name
}
set { _Name = value }
}
Okay, so I wrote this program from an exercise in a C# programming book (I'm trying to learn here) and it asks for "Override the ToString() method to return all data members".
Have I done this correctly? Or have I just successfully written code that compiles but does nothing? What is the purpose of ToString?
I have spent about 30 minutes looking at other posts on this and haven't figured it out, so I decided to make this.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication297
{
class Program
{
static void Main(string[] args)
{
String name = "Stormtrooper";
Employee s = new Employee(name);
Console.WriteLine("The type of hire is a {0}", s.Name);
Console.WriteLine("The identification number is {0}", s.Number);
Console.WriteLine("The date of hire is {0} ABY", s.Date);
Console.WriteLine("The standard galactic salary is...{0:C}", s.Salary);
}
class Employee
{
private string _name;
private string _number;
private int _date;
private int _salary;
public string Name
{
get
{
return _name;
}
}
public string Number
{
get
{
return _number;
}
}
public int Date
{
get
{
return _date;
}
}
public int Salary
{
get
{
return _salary;
}
}
public Employee(string n)
{
_name = n;
_number = "AA23TK421";
_date = 4;
_salary = 800;
}
}
public override string ToString()
{
return "_name + _number + _date + _salary".ToString();
}
}
}
You are returning a string that just says the phrase _name + _number + _date + _salary.
What you likely wanted to do is build a string using those fields. If you wanted them all mushed together Concat would work, but it would be highly un-readable
public override string ToString()
{
return String.Concat(_name, _number, _date, _salary);
}
However what would be better is to use Format and include labels with the values
public override string ToString()
{
return String.Format("Name:{0}, Number:{1}, Date:{2}, Salary:{3}",_name, _number, _date, _salary);
}
If you are using C# 6 or newer you can use the following cleaner format
public override string ToString()
{
return $"Name:{_name}, Number:{_number}, Date:{_date}, Salary:{_salary}";
}
Which is the exact same logic as the previous String.Format version.
The reason people override the ToString() method is to have a default string representation of your object, usually for display to the user or in a log or console, like this:
Console.WriteLine(yourClassObject);
If you do not override the ToString(), then its default implementation is to return the fully qualified name of your object, like this:
YourNamespace.YourClassName
By changing the inherited implementation (from System.Object), then you can make a nicer (read: prettier) representation, like this:
public override string ToString()
{
return String.Format("This instance of my object has the following: Name = {0}, Number = {1}, Date = {2}, Salary = ${3}", _name, _number, _date, _salary);
}
If you are using C# 6 (or later) use the nameof() method for the property names in the string in case the property names change. You can also use the $"" notation instead of using string.Format().
For example:
public override string ToString()
{
return $"{nameof(Name)}: {_name}";
}
Rather try something like
public override string ToString()
{
return String.Format("Name : {0}, number {1}, date {2}, salary {3}",_name,_number,_date,_salary);
}
But it neads to be part of the class
so
class Employee
{
private string _name;
private string _number;
private int _date;
private int _salary;
.....
public override string ToString()
{
return String.Format("Name : {0}, number {1}, date {2}, salary {3}",_name,_number,_date,_salary);
}
}
Have a look at String.Format Method
Replaces each format item in a specified string with the text
equivalent of a corresponding object's value.
You could try to format the output in a nice format. (not tested, though)
public override string ToString()
{
return string.Format("Name: {0} Number: {1:n0} Date: {2:yyyy-MM-dd} Salary: {3:n2}", _name, _number, _date, _salary);
}
there are a lot of purposes overwriting .ToString(), depending on the context. for example,
some developers like to have nicely formatted object description when doing debug, overwriting .ToString() would allow them to have meaningful description with some identifier (for example, the Id of a object);
Some developers like to put some serialization code into the ToString() method;
Some developers even put some debug code into the .ToString() method, though it might not be a good practice.
it really depending on the context of your needs. you may find some good practices to follow online - believe there are plenty of resources online.
Without overiding ToString, if you tried to "get" the string value of an Employee, e.g.
var employee1= new Employee();
Console.WriteLine(employee1);
What you'd get is:
ConsoleApplication1.Program+Employee
Which provides no information at all to help you (or a UI) display relevant information.
I use
return _name + _number + _date + _salary;
Which defaults to string,
or a more verbose
return "Name:" + _name + " Number:" + _number + " etc...";
class Program
{
static void Main( )
{
int Number = 10;
Console.WriteLine(Number.ToString());
Customer cc = new Customer();
cc.FirstName = "Rakibuz";
cc.LastName = "Sultan";
Console.WriteLine(Convert.ToString(cc));
}
}
public class Customer
{
public string FirstName;
public string LastName;
public override string ToString()
{
return FirstName + " " + LastName;
}
}
I am new to C# and I am creating a Form that is supposed to give the user the opportunity to enter name and age. Then, by submitting this information, there should be a summary(a new form) that shows what the user their input.
I finally got it working with the name, as it's a string and it's not a big deal, but I am stuck with the age.
I've tried casting, however, it does not work. I also looked at the documentation, but I do not find anything useful. Well, probably because I don't know where to look.
Anyway, I would strongly appreciate if someone give me an example for this.
Thanks in advance.
FormEnterDetails.cs
PersonStatic.LName = this.textBoxLastName.Text;
PersonStatic.Age = this.textBoxAge.Text;
DetailsHolder.cs
private string lName;
public string LName
{
get { return lName; }
set { lName = value; }
}
string age;
public String Age
{
get { return age; }
set { age = value; }
}
FormSummary.cs
private void FormSummary_Load(object sender, EventArgs e)
{
//we need to do this work on form load and not on creation
this.labelFirstNameSummary.Text = dh.FName;
this.labelLastNameSummary.Text = dh.LName;
this.labelAge.Text = Int32.Parse(dh.Age);
}
PersonStatic.cs
static string lName;
public static string LName
{
get { return PersonStatic.lName; }
set { PersonStatic.lName = value; }
}
static string age;
public static string Age
{
get { return PersonStatic.age;}
set { PersonStatic.age = value; }
}
I hope you want something like this
string ageString = ageInt.ToString();
Use the ToString() built-in function to convert anything to a string:
Int x=5;
String y;
y=x.ToString();
From the code you've posted, all places related to age appear to already be using string, not int (note that these are keywords that shorten the real type names, System.String and System.Int32). So you should be getting an error on this line:
this.labelAge.Text = Int32.Parse(dh.Age);
The Int32.Parse static method converts from a string to an int. (If you want to convert the other way, then as other answers have mentioned, you can call the ToString() instance method on your int.)
But in this case dh.Age, assuming dh is an instance of DetailsHolder, is already a string. And labelAge.Text, assuming labelAge is an instance of System.Windows.Forms.Label, is also a string. So you don't need to do any conversion:
this.labelAge.Text = dh.Age;
I have a bunch of business class with autoproperties :
public class A {
public int Id { get; set; }
public string Title { get; set;}
}
Because the application evolves, there is a new requirement to enable tracking the changes of the properties, in order to send to the backing store only changed data.
In order to reach this goal, I have to convert ALL properties to field + property like this :
public class A {
private int m_Id;
public int Id {
get { return m_Id; }
set {
if(m_Id != value){
SetChanged("Id");
m_Id = value;
}
}
}
private string m_Title;
public string Title
{
get { return m_Title; }
set {
if(m_Title != value){
SetChanged("Title");
m_Title = value;
}
}
}
protecte void SetChanged(string propertyName) {
// Not important here
}
}
Is there a way to quickly refactor my code to avoid having to manually change the properties ?
There's no way in the IDE to do this, but if you need to replace all X properties, I would write a short console application to do it.
The process would be:
Iterate over all files in directory matching *.cs
Foreach file, regex find and replace old property for new property syntax
Using regex to match is very powerful. Regex can be used in VS2010 to do a find/replace operation. If you try finding this (with regex enabled)
{(public|private|internal|protected)}:b{[a-zA-Z0-9]+}
:b{[a-zA-Z0-9]+}:b\{ get; set; \}
It will match properties like this
public Type Foo { get; set; }
In your console application find all lines of code that match the above, then start splitting them up into Modifier, Type, Property Name and finally replacing the whole block with something like this
// PS: this is pseudocode ;-) or could be your new property template
private [Type] m_[PropertyName].ToPascaleCase
public [Type] PropertyName
{
get { return m_[PropertyName].ToPascaleCase; }
set
{
if(m_[PropertyName].ToPascaleCase != value){
SetChanged([PropertyName]);
m_[PropertyName].ToPascaleCase = value;
}
}
}
Finally I would advocate taking a backup of your code or running this test offline and testing before checking in!!
You can always just create a generic method that will do the assignment and call SetChange
void SetChangeIfNeeded<T>(ref T field, T value, string propertyName)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
SetChanged(property);
}
}
You would still need to have a private back field. Your class would look something like:
public class A {
private int m_id
public int Id
{
get { return m_id };
set { SetChangeIfNeeded<int>(ref m_id, value, "Id"); }
}
}
ReSharper can do this, but wouldn't modify setter.
public string Title {
get { return m_title; }
set { m_title = value; }
}
There is probably no direct way of doing this with refraction. If this was my problem. I would make code to generate this:
public string MakePropertyBigger(string varName, string propName, string dataType)
{
string output = "";
output += string.Format("private {0} {1};", dataType, varName) + Environment.NewLine;
output += string.Format("public {0} {1}", dataType, propName) + Environment.NewLine;
output += "{" + Environment.NewLine;
output += string.Format("get { return {0}; }", varName) + Environment.NewLine;
output += string.Format("set { if({0} != value){ SetChanged(\"{1}\");", varName, propName) + Environment.NewLine;
output += string.Format("{0} = value; }", varName) + Environment.NewLine;
output + "}" + Environment.NewLine + "}";
Now just plug this in and chug it out.
I've got something like this in my property/accessor method of a constructor for my program.
using System;
namespace BusinessTrips
{
public class Expense
{
private string paymentMethod;
public Expense()
{
}
public Expense(string pmtMthd)
{
paymentMethod = pmtMthd;
}
//This is where things get problematic
public string PaymentMethod
{
get
{
return paymentMethod;
}
set
{
if (string.IsNullOrWhiteSpace(" "))
paymentMethod = "~~unspecified~~";
else paymentMethod = value;
}
}
}
}
When a new attribute is entered, for PaymentMethod, which is null or a space, this clearly does not work. Any ideas?
do you perhaps just need to replace string.IsNullOrWhiteSpace(" ") with string.IsNullOrWhiteSpace(value) ?
From your posted code, you need to call:
this.PaymentMethod = pmtMthd;
instead of
paymentMethod = pmtMthd;
The capital p will use your property instead of the string directly. This is why it's a good idea to use this. when accessing class variables. In this case, it's the capital not the this. that makes the difference, but I'd get into the habit of using this.
Jean-Barnard Pellerin's answer is correct.
But here is the full code, which I tested in LinqPad to show that it works.
public class Foo {
private string _paymentMethod = "~~unspecified~~";
public string PaymentMethod
{
get
{
return _paymentMethod;
}
set
{
if (string.IsNullOrWhiteSpace(value))
_paymentMethod = "~~unspecified~~";
else _paymentMethod = value;
}
}
}
With a main of:
void Main()
{
var f = new Foo();
f.PaymentMethod = "";
Console.WriteLine(f.PaymentMethod);
f.PaymentMethod = " ";
Console.WriteLine(f.PaymentMethod);
f.PaymentMethod = "FooBar";
Console.WriteLine(f.PaymentMethod);
}
Output from console:
~~unspecified~~
~~unspecified~~
FooBar