C# Design – Managing Many Properties, Complex Constructors, and Equality

cdesign

I'm still a relative novice with OOP, so I am still trying to understand some of the best practices for how to design "good" objects. I know this question is probably a rather subjective, which is why I am here rather than on stackoverflow.

I am designing an object that will have 20 separate string properties, none of which are required. These constraints are fixed and cannot be changed as it is an implementation of an outside data source that I have no control over.

The design problem I am running into is how to effectively construct an instance of the object and be able to compare equality by comparing each field without violating normal best practices.

  • A 20 argument constructor seems very wrong, especially since every field is a string, and there are cases where many of them will be null. I could use the optional arguments in C#4 to reduce the number of arguments that are actually called, but since everything is a string, I would have to use named parameters when calling the constructor, and that seems that it would be even uglier.

  • I have thought about trying to group the properties into smaller classes, but the most logical grouping still has an object with 10 properties, and at least half of the remaining properties have no logical grouping.

  • I could give each property a public setter, but that would make the object mutable, and thus complicate matters for overriding GetHashCode(). And I really don't need the object to be mutable. Once it is built, there should never be a reason to change it, but I don't want to rely on that because who knows what someone will try to do with it.

  • Implementing IEquatable<T> is a possibility for the equality test, but my understanding is it is recommended to also override Equals(object obj) and GetHashCode() when implementing IEquatable so all Equals methods have the same behavior.

Is this an edge case where overriding Equals without overriding GetHashCode should be done? I don't intend on using this object as a key in a dictionary or as a member of a HashSet, but I can't predict what someone else would try to do in the future.

Or should I just abandon the idea of doing an equality comparison on all of the properties with Equals and implement my own Compare() method?

Or is there a better method for building the object that would allow it to still be immutable without having to pass all of the arguments through a constructor or a method?

EDIT:
In response to Sign's comment, the purpose of the object is ultimately to be able to compare the data from the outside data source to our internal data to ensure it is accurate. Both data sets contain address information, but they are broken up differently.

The internal data, is generally stored as a typical address (Street, City, State, Postal Code, County, Country), but there are variations when you have a suite/apartment # or a floor in a building.

The external data is broken down even further into smaller pieces.

So this object is supposed to be a common link between the 2 data sources and allow the user to compare like fields for equality.

EDIT #2:
Ultimately, I chose Doc Brown's answer because a modified version of his dictionary idea (with Enums as Keys instead of strings) was the simplest way to go without having a messy constructor and still provide the immutability I was hoping to have. In the long term, I am going to experiment with some of Oliver's suggestions as some of his edits did have a lot to offer.

Best Answer

If you insist of having your object to immutable, there is obviously no other way than providing the 20 strings through the constructor. And if you want to be able to leave some of the strings out, you must say which arguments you are providing and which are not, which leads you to some form of named parameters. Of course, besides the possibilities you mentioned by yourself, you can also

  • provide the 20 arguments by a list of strings
  • provide them by a dictionary (key=>value, where "key" is the attribute name)
  • provide them by an object of a helper class, which has the same 20 properties with getters and setters, so this one won't be immutable

It may also be a good idea to implement Equals and GetHashCodeby utilizing reflection, looping over all public string properties of your class.

Related Topic