changing class property value outside of the class - c#

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 }
}

Related

Optimize the Algotithm

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.

Adding a bool for each property

I'm building a c# class that works with two different data sources. It will load a data source and take a configuration set from a function. Then I want to do several tasks on all properties within the object.
for example.
public String StreetAddress
{
get { return _streetAddress; }
set
{
if (value.Length <= 64)
_streetAddress = value;
else
_streetAddress = value.Substring(0, 1024).Trim();
}
}
public String City
{
get { return _city; }
set
{
if (value.Length <= 128)
_city = value;
else
_city = value.Substring(0, 128).Trim();
}
}
public String State
{
get { return _state; }
set
{
if (value.Length <= 128)
_state = value;
else
_state = value.Substring(0, 128).Trim();
}
}
So that holds the data from one side. I was hoping to be able to store and set a change flag on each property. So if we take State for example. If the person is moved from Texas to Illinois I want to set a bool within that property to note the change then be able to loop over all changes before saving the object to the DB. But I don't see any way to assign another state variable within that property. Is the best way to write another object on top of this to control it or is there another more creative way to store multiple strings within the one property?
If you'd like an OOP way of doing the thing, you can:
Define an interface and a class for holding your property, such as:
interface IPropertySlot
{
bool IsDirty { get; }
void ResetIsDirty();
object UntypedValue { get; }
}
class PropertySlot<T>:IPropertySlot
{
public T Value { get; private set; }
public bool SetValue(T value)
{
if (!Equals(_value, Value))
{
Value = value;
IsDirty = true;
return true;
}
return false;
}
public bool IsDirty { get; private set; }
public void ResetIsDirty()
{
IsDirty = false;
}
public object UntypedValue
{
get { return Value; }
}
}
Store your properties inside your class in a dictionary from String (for name of property) to IPropertySlot and get/set them through a pair of methods:
void SetProperty<T>(string name, T value)
{
IPropertySlot property;
if (!_properties.TryGetValue(name, out property))
{
property = new PropertySlot<T>();
_properties[name] = property;
}
((PropertySlot<T>)property) .SetValue(value);
}
T GetProperty<T>(string name)
{
IPropertySlot property;
if (!_properties.TryGetValue(name, out property))
{
property = new PropertySlot<T>();
_properties[name] = property;
}
return ((PropertySlot<T>)property).Value;
}
Finding the changed properties later is just a matter of going over the _properties.Values and finding which of them are IsDirty.
This approach also gives you a way to add more functionality to your properties in an OO manner (such as raising PropertyChanged/PropertyChanging events, mapping it to DB fields, etc.).
In such a situation I'd prefer an approach external to the Dto implementation.
Implement some unit that would take two instances of a class, and determine all the differences.
Map each property to compare:
static PropertyManager<Dto> manager = new PropertyManager<Dto>()
.Map(x => x.City)
.Map(x => x.StreetAddress);
Use two instances to compute difference:
var a = new Dto{ StreetAddress = "Foo", City = "Bar" };
var b = new Dto{ StreetAddress = "Foo", City = "Baz" };
var differences = manager.ComputeDifferences(a,b).ToList();
if( differences.Any() )
{
Console.WriteLine("Instances differ");
}
foreach (var diff in differences)
{
Console.WriteLine(diff);
}
This sample code prints out:
Instances differ
x.City
Here is a complete code example:
https://dotnetfiddle.net/4sNeoN

Trying to get/set record

I am having problems getting and setting a value because I have introduced a third aspect into my code.
Previously I would do this to get/set in the record:
public virtual string MyString { get; set;}
and then in my part:
public string MyString
{
get { return Record.MyString; }
set { Record.MyString = value; }
}
and NHibernate would save my values in the DB (obviously my other code is not presented here for the sake of brevity).
Now I am trying to do a checkbox with a list. I have this for a checkbox:
public class MyPart : ContentPart<MyPartRecord>
{
public MyPart()
{
MyList = Enum.GetValues(typeof(MyEnum))
.Cast<MyEnum>().Select(x =>
{
return new SelectListItem { Text = x.ToString().ToUpper(),
Value = ((int)x).ToString() };
}).ToList();
}
public IList<SelectListItem> MyList { get; set; }
private string myCheckBox;
// Record class contains the following commented code:
// public virtual string MyCheckBox { get; set;}
// Trying to do this now here in MyPart class:
public string MyCheckBox
{
get
{
if (!string.IsNullOrEmpty(myCheckBox))
return myCheckBox;
// Tried the following commented code to get value:
// Record.MyCheckBox = myCheckBox;
return string.Join(",", MyList.Where(x => x.Selected)
.Select(x => x.Value).ToArray());
}
set
{
myCheckBox = value;
// Tried the following commented code to set value:
// Record.MyCheckBox = myCheckBox;
}
}
}
I just don't know how to assign the values in this case (getting/setting myCheckBox into MyCheckBox. It's being saved in the DB as null.
Thanks in advance for any help.
It seems to me you'd be hiding the base virtual implementation of MyCheckBox.
I think instead you'd rather override the base:
public override String MyCheckBox
{
get
{
if (!string.IsNullOrEmpty(myCheckBox))
return myCheckBox;
// Tried the following commented code to get value:
// Record.MyCheckBox = myCheckBox;
return string.Join(",", MyList.Where(x => x.Selected)
.Select(x => x.Value).ToArray());
}
set
{
myCheckBox = value;
// Tried the following commented code to set value:
// Record.MyCheckBox = myCheckBox;
}
}
Thereby becoming the variable instead of obfuscating it?
I wound up just getting rid of the part and sticking with only a record - this allowed me to do the get and set in a view model, which worked out better (and was less confusing).

C# Automatically linking strings to properties using the string value

This might be a stupid one but I'll shoot it out there.
For example let's say I have a model class:
public class PermissionModel
{
public bool AppName_Home_Product_SaveButton_Enabled { get; set; }
public bool AppName_Home_Product_ConfirmButton_Enabled { get; set; }
}
And I have the following list of strings:
"AppName_Home_Product_SaveButton_Enabled_true"
"AppName_Home_Product_SaveButton_Enabled_false"
I want to automatically populate the model properties with true/false without having to use if statements as in the following example:
if (aString.Contains("AppName_Home_Product_SaveButton_Enabled"))
{
PermissionModel.AppName_Home_Product_SaveButton_Enabled = Convert.ToBoolean(AString.Substring(AString.IndexOf("Enabled_") + 8));
}
Any ideas or is this crazy? I just want to avoid a bunch of if statements to populate the model and make it more re-usable.
This can be done via reflection
const string delimiter = "_Enabled";
foreach (string data in aString) {
int index = data.IndexOf(delimiter);
if (index >= 0) {
// Get the name and value out of the data string
string name = data.Substring(0, index + delimiter.Length);
bool value = Convert.ToBoolean(data.Substring(index + delimiter.Length + 1));
// Find the property with the specified name and change the value
PropertyInfo property = GetType().GetProperty(name);
if (property != null) {
property.SetValue(this, value);
}
}
}

Replace a collection item using Linq

How do I find and replace a property using Linq in this specific scenario below:
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
//TODO: Just copying values... Find out how to find the index and replace the value
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
Thanks for helping out in advance.
Do not use LINQ because it will not improve the code because LINQ is designed to query collection and not to modify them. I suggest the following.
// Just realized that Array.IndexOf() is a static method unlike
// List.IndexOf() that is an instance method.
Int32 index = Array.IndexOf(this.Properties, name);
if (index != -1)
{
this.Properties[index] = value;
}
else
{
throw new ArgumentOutOfRangeException();
}
Why are Array.Sort() and Array.IndexOf() methods static?
Further I suggest not to use an array. Consider using IDictionary<String, Property>. This simplifies the code to the following.
this.Properties[name] = value;
Note that neither solution is thread safe.
An ad hoc LINQ solution - you see, you should not use it because the whole array will be replaced with a new one.
this.Properties = Enumerable.Union(
this.Properties.Where(p => p.Name != name),
Enumerable.Repeat(value, 1)).
ToArray();
[note: this answer was due to a misunderstanding of the question - see the comments on this answer. Apparently, I'm a little dense :(]
Is your 'Property' a class or a struct?
This test passes for me:
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
[TestMethod]
public void TestMethod1()
{
var pb = new PropertyBag() { Properties = new Property[] { new Property { Name = "X", Value = "Y" } } };
Assert.AreEqual("Y", pb["X"].Value);
pb["X"] = new Property { Name = "X", Value = "Z" };
Assert.AreEqual("Z", pb["X"].Value);
}
I have to wonder why the getter returns a 'Property' instead of whatever datatype .Value, but I'm still curious why you're seeing a different result than what I am.

Categories