I find the following bug occurring far too often in my code and wondered if anyone knows some good strategies to avoid it.
Imagine a class like this:
public class Quote
{
public decimal InterestRate { get; set; }
}
At some point I create a string that utilises the interest rate, like this:
public string PrintQuote(Quote quote)
{
return "The interest rate is " + quote.InterestRate;
}
Now imagine at a later date I refactored the InterestRate property from a decimal to its own class:
public class Quote
{
public InterestRate InterestRate { get; set; }
}
... but say that I forgot to override the ToString method in the InterestRate class. Unless I carefully looked for every usage of the InterestRate property I would probably never notice that at some point it is being converted to a string. The compiler would certainly not pick this up. My only chance of saviour is through an integration test.
The next time I call my PrintQuote method, I would get a string like this:
"The interest rate is Business.Finance.InterestRate".
Ouch. How can this be avoided?
By creating an override of ToString in the IntrestRate class.
The way to prevent this kind of problem is to have a unit test for absolutely all your class members, which therefore includes your PrintQuote(Quote quote) method:
[TestMethod]
public void PrintQuoteTest()
{
quote = new Quote();
quote.InterestRate = 0.05M;
Assert.AreEqual(
"The interest rate is 0.05",
PrintQuote(quote));
}
In this case, unless you defined a implicit conversion between your new InterestRate class and System.Decimal, this unit test would actually no longer compile. But that would definitely be a signal! And if you did define an implicit conversion between your InterestRate class and System.Decimal, but forgot to override the ToString method, then this unit test would compile, but would (correctly) fail at the Assert.AreEqual() line.
The need for having a unit test for absolutely every class member cannot be overstated.
Creating an override of ToString is just one of those things you do for most, if not all, classes. Certainly for all "value" classes.
Note that ReSharper will generate a lot of the boilerplate code for you. From:
public class Class1
{
public string Name { get; set; }
public int Id { get; set; }
}
The result of running Generate Equality Members, Generate Formatting Members and Generate Constructor is:
public class Class1 : IEquatable<Class1>
{
public Class1(string name, int id)
{
Name = name;
Id = id;
}
public bool Equals(Class1 other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return Equals(other.Name, Name) && other.Id == Id;
}
public override string ToString()
{
return string.Format("Name: {0}, Id: {1}", Name, Id);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != typeof (Class1))
{
return false;
}
return Equals((Class1) obj);
}
public override int GetHashCode()
{
unchecked
{
return ((Name != null ? Name.GetHashCode() : 0)*397) ^ Id;
}
}
public static bool operator ==(Class1 left, Class1 right)
{
return Equals(left, right);
}
public static bool operator !=(Class1 left, Class1 right)
{
return !Equals(left, right);
}
public string Name { get; set; }
public int Id { get; set; }
}
Note there is one bug: it should have offered to create a default constructor. Even ReSharper can't be perfect.
Not to be a jerk but write a test case each time you create a class. It is a good habit to get into and avoids oversights for you and others participating in your project.
Well, as others have said, you just have to do it. But here are a couple of ideas to help yourself make sure you do it:
1) use a base object for all of your value classes that overrides toString and, say, throws an exception. This will help remind you to override it again.
2) create a custom rule for FXCop (free Microsoft static code analysis tool) to check for toString methods on certain types of classes. How to determine which types of classes should override toString is left as an exercise for the student. :)
In the case where ToString is called on something statically typed as an InterestRate, as in your example, or in certain related cases where an InterestRate is cast to Object and then immediately used as a parameter to something like string.Format, you could conceivably detect the problem with static analysis. You could search for a custom FxCop rule that approximates what you want, or write one of your own.
Note that it will always be possible to devise a sufficiently dynamic call pattern that it breaks your analysis, probably not even a very complicated one ;), but catching the lowest-hanging fruit should be easy enough.
That said, I agree with some of the other commenter that thorough testing is probably the best approach to this specific problem.
For a very different perspective, you could defer all ToString'ing to a separate concern of your application. StatePrinter (https://github.com/kbilsted/StatePrinter) is one such API where you can use the defaults or configure depending on types to print.
var car = new Car(new SteeringWheel(new FoamGrip("Plastic")));
car.Brand = "Toyota";
then print it
StatePrinter printer = new StatePrinter();
Console.WriteLine(printer.PrintObject(car));
and you get the following output
new Car() {
StereoAmplifiers = null
steeringWheel = new SteeringWheel()
{
Size = 3
Grip = new FoamGrip()
{
Material = ""Plastic""
}
Weight = 525
}
Brand = ""Toyota"" }
and with the IValueConverter abstraction you can define how types are printer, and with the FieldHarvester you can define which fields are to be included in the string.
Frankly, the answer to your question is that your initial design was flawed. First, you exposed a property as a primitive type. Some believe this is wrong. After all, your code allows this ...
var double = quote.InterestRate * quote.InterestRate;
The problem with that is, what is the unit of the result? Interest^2? The second problem with your design is that you rely on an implicit ToString() conversion. Problems with relying on implicit conversion are more well known in C++ (for example), but as you point out, can bite you in C# as well. Perhaps if your code originally had ...
return "The interest rate is " + quote.InterestRate.ToString();
... you would have noticed it in the refactor. Bottom line is if you have design issues in your original design, they might be caught in refactor and the might not. Best bet is to not do them in the first place.
Related
Perhaps I am demonstrating my ignorance of some oft-used feautre of C# or the .NET framework, but I would like to know if there is a natively-supported way to create a type alias like EmailAddress which aliases string but such that I can extend it with my own methods like bool Validate()?
I know of the using x = Some.Type; aliases but these are not global nor do they provide type safety, i.e. one could swap out an ordinary string for the using alias in the current file. I would like my EmailAddress to be its own type, independent and not interchangeable with the string type that it shadows.
My current solution is to generate public sealed partial EmailAddress : IEquatable<EmailAddress>, IXmlSerializable classes with a T4 template generating the boilerplate implicit string conversion operators and other such things. This is fine with me for now and gives me a lot of flexibility but at the back of my mind it seems silly that I have to generate such copious amounts of boilerplate code to do something as simple as creating a strong type alias.
Maybe this is not possible other than with code generation, but I am curious if others have attempted something similar with their designs and what your experiences have been. If nothing else, perhaps this could serve as a good use-case for such an alias feature in a hypothetical future version of C#. Thanks!
EDIT: The real value that I want out of this is to be able to get type safety with primitive types that represent different types/formats for data. For instance, an EmailAddress and a SocialSecurityNumber and a PhoneNumber, all of which use string as their underlying type but which are not interchangeable types in and of themselves. I think this gets you much more readable and self-documenting code, not to mention added benefits of more method overload possibilities that are less ambiguous.
If you look at the .NET Framework System.Uri is the closest example that is similar to an email address. In .NET the pattern is to wrap something in a class and add constraints that way.
Adding strong typing that adds additional constraints to simple types is an interesting language feature that I believe some functional language has. I can't recall the name of the language which would let you add dimensional units such as feet to your values and do a dimensional analysis on your equations to ensure that the units matched.
Some background on why string is sealed:
From http://www.code-magazine.com/Article.aspx?quickid=0501091 :
Rory: Hey Jay, you mind if I ask you a
couple questions? I'm already curious
about some things. First of all, and
this was brought up at one of the MSDN
events I did this week, why is String
sealed? Note: for VB.NET programmers,
Sealed = NotInheritable.
Jay: Because we do a lot of magic
tricks in String to try and make sure
we can optimize for things like
comparisons, to make them as fast as
we possibly can. So, we're stealing
bits off of pointers and other things
in there to mark things up. Just to
give you an example, and I didn't know
this when I started, but if a String
has a hyphen or an apostrophe in it
[then] it sorts differently than if it
just has text in it, and the algorithm
for sorting it if you have a hyphen or
an apostrophe if you're doing
globally-aware sorting is pretty
complicated, so we actually mark
whether or not the string has that
type of behavior in it.
Rory: So, what you're saying is that
in the string world, if you didn't
seal String there would be a whole lot
of room for wreaking a lot of havoc if
people were trying to subclass it.
Jay: Exactly. It would change the
entire layout of the object, so we
wouldn't be able to play the tricks
that we play that pick up speed.
Here is the CodeProject article that you probably have seen before:
http://www.codeproject.com/KB/cs/expandSealed.aspx
So yeah, implicit operator is your only solution.
Does the System.Net.Mail.MailAddress class fit your needs, or at least "help"?
EDIT: It's not explicitly IEquatable or ISerializable, but you could easily enough add those in your own wrapper.
It seems you have at least a reasonable C# knoledgde so my answer may seem stupid, but what you want is called "type hierarchy" and the guys who coded the String class wanted to prevent you from using this "OO feature" so they made String class sealed, that's why you won't be able to do what you want. The best approach is this you are on now: Make your own type and an implicit convertion to String.
I think you want to use extension methods. They allow you to extend a classes functionality without creating a new derived type.
I guess I do not get why you want to have both strong types AND implicit string conversion at the same time. For me, one rules out the other.
I tried to solve the same problem for ints (you mention int in the title, but not in the question). I found that declaring an enum gives you a type-safe integer which needs to be explicitly cast from/to int.
Update
Enums may not be intended for open sets, but can still be used in such a way. This sample is from a compilation experiment to distinguish between the ID columns of several tables in a database:
enum ProcID { Unassigned = 0 }
enum TenderID { Unassigned = 0 }
void Test()
{
ProcID p = 0;
TenderID t = 0; <-- 0 is assignable to every enum
p = (ProcID)3; <-- need to explicitly convert
if (p == t) <-- operator == cannot be applied
t = -1; <-- cannot implicitly convert
DoProc(p);
DoProc(t); <-- no overloaded method found
DoTender(t);
}
void DoProc(ProcID p)
{
}
void DoTender(TenderID t)
{
}
I made this class to cover identical needs. This one is for the type "int" (I also have one for "string"):
public class NamedInt : IComparable<int>, IEquatable<int>
{
internal int Value { get; }
protected NamedInt() { }
protected NamedInt(int val) { Value = val; }
protected NamedInt(string val) { Value = Convert.ToInt32(val); }
public static implicit operator int (NamedInt val) { return val.Value; }
public static bool operator ==(NamedInt a, int b) { return a?.Value == b; }
public static bool operator ==(NamedInt a, NamedInt b) { return a?.Value == b?.Value; }
public static bool operator !=(NamedInt a, int b) { return !(a==b); }
public static bool operator !=(NamedInt a, NamedInt b) { return !(a==b); }
public bool Equals(int other) { return Equals(new NamedInt(other)); }
public override bool Equals(object other) {
if ((other.GetType() != GetType() && other.GetType() != typeof(string))) return false;
return Equals(new NamedInt(other.ToString()));
}
private bool Equals(NamedInt other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(Value, other.Value);
}
public int CompareTo(int other) { return Value - other; }
public int CompareTo(NamedInt other) { return Value - other.Value; }
public override int GetHashCode() { return Value.GetHashCode(); }
public override string ToString() { return Value.ToString(); }
}
And to consume it in your case:
public class MyStronglyTypedInt: NamedInt {
public MyStronglyTypedInt(int value) : base(value) {
// Your validation can go here
}
public static implicit operator MyStronglyTypedInt(int value) {
return new MyStronglyTypedInt(value);
}
public bool Validate() {
// Your validation can go here
}
}
If you need to be able to serialize it (Newtonsoft.Json), let me know and I'll add the code.
I have the following object (class).
namespace Temp.Models
{
public class CurrentClass
{
private double _firstCoefficient;
private double _secondCoefficient;
public double FirstCoefficient
{
get { return _firstCoefficient; }
set { _firstCoefficient= value; }
}
public double SecondCoefficient
{
get { return _secondCoefficient; }
set { _secondCoefficient= value; }
}
}
}
The following class utilizes the above object and therefore initializes the object as follows:
namespace Temp.Models
{
public class MainClass
{
private CurrentClass _currentClass = new CurrentClass();
public CurrentClass CurrentClass
{
get { return _currentClass; }
set { _currentClass = value; }
}
}
}
At some point if certain conditions are met I would define the variables as follows:
MainClass currentObject = new MainClass();
//if conditions are met
currentObject.CurrentClass.FirstCoefficient = 0;
currentObject.CurrentClass.SecondCoefficient = 5;
But what if the conditions are never met and I never define the above variables. How and/or what is the best way to check if the object was never defined?
I can do the following check:
if(currentObject.CurrentClass.FirstCoefficient != 0 && currentObject.CurrentClass.SecondCoefficent != 0)
But the values can be defined as 0...So I am not sure how to go about this.
Any help is much appreciated!
These are some principles that can be used for solving the problem with description, samples and brief evaluation/opinion.
1. Parametrization through constructors
According to OOP principles, a constructor is method used to initialize an object to a valid state. The concept of immutability takes this even further, disallowing any changes, completely avoiding invalid state.
There is also a possibility of compromise where the API of an object disallows invalid states.
With this concept, you would arrive to:
namespace Temp.Models
{
public class CurrentClass
{
public double FirstCoefficient { get; private set; }
public double SecondCoefficient { get; private set; }
public CurrentClass(double firstCoefficient, double secondCoefficient)
{
FirstCoefficient = firstCoefficient;
SecondCoefficient = secondCoefficient;
}
// if mutability is required - this is needless as the constructor is
// the same but if there was more complex state, methods like this would make
// sense, mutating only parts of the state
public void SetCoefficients(double firstCoefficient, double secondCoefficient)
{
FirstCoefficient = firstCoefficient;
SecondCoefficient = secondCoefficient;
}
}
}
Summary:
Each instantiation of CurrentClass is always in a valid state, avoiding a lot of consistency checks (improved encapsulation)
It takes more code to write (but you save a lot of other code due to the previous point)
You need to know the coefficients beforehand.
2. Using nullable types
Nullable types add the "additional" value to types, the "undefined" state. Reference types (class) are nullable by design while value types (struct) need to be marked nullable, either as Nullable<T> or with the shorthand T?.
This then allows the objects be in invalid state and be specific about it. This goes to the other end of consistency scale from immutability as an object with multiple nullable fields has many invalid states.
Sample code:
namespace Temp.Models
{
public class CurrentClass
{
public double? FirstCoefficient { get; set; }
public double? SecondCoefficient { get; set; }
}
}
Now this gets instantiated quite nicely and can be changed on the fly:
public CurrentClass CreateCurrentClass()
{
var currentClass = new CurrentClass { FirstCoefficient = 1.0 };
var secondCoefficient = RetrieveSecondCoefficient();
currentClass.SecondCoefficient = secondCoefficient;
return currentClass;
}
You'll however need validity checks everywhere the object is used.
public bool IsValid(CurrentClass currentClass)
{
// what if FirstCoefficient has value and SecondCoefficient doesn't,
// is that always an invalid state?
return currentClass.FirstCoefficient.HasValue
&& currentClass.SecondCoefficient.HasValue;
}
Summary:
Very little code is needed to have a DTO up and running
A lot of consistency checks (and related brain pain) are required to work with such model
Encapsulation is lacking - any method taking CurrentClass can alter its validity, therefore making the previous point even worse. This can be eased by usage of read-only interface passed where read-only access is required.
Summing up
There are many other means that usually lay in between the two aforementioned approaches. For example you can use one validity flag (SergeyS's response) per object and ease on the external validity checks but having more code in the class and the need of deeper thinking.
Personally, I prefer immutability. It's more monkey code to write but will definitely pay off down the road thanks to the clean design.
A complex system without immutability is very hard to reason about without extensive knowledge. This is especially painful when working in a team - usually each person only knows a part of the codebase.
The sad thing is that it's not always possible to have evertything immutable (e.g. viewmodels): then I tend to convert objects to an internal immutable model as soon as it's possible.
Given what you already wrote, I would add Initialize() method and Initialized property into your MainClass class. Something similar to this:
public class MainClass
{
private CurrentClass _currentClass = new CurrentClass();
public CurrentClass CurrentClass
{
get { return _currentClass; }
set { _currentClass = value; }
}
public bool Initialized {get; private set;}
public void Initialize()
{
this.CurrentClass.FirstCoefficient = 0;
this.CurrentClass.SecondCoefficient = 5;
this.Initialized = true;
}
}
Call Initialize() method where your conditions met.
Later in code you can just check if(currentObject.Initialized). Notice private setter for `Initialized' property, it will ensure this flag was not accidentally set by external code.
Depending on your needs, you can go further and pass parameters for initialization directly to Initialize() method as parameters.
You have several approaches, like force values to be correct in constructor or have another variable telling if object has no value yet, like System.Drawing.Point has static "Empty" property. But in this case of your simple object your main class is explicitly creating an instance of CurrentClass so at this point this object should be correct and coefficients should be set. If you rely on some other code to set those values to perform some other action later, it is out of scope of these two objects here.
Update: perharps sharing details of what the real problem is would be better, because I have a feeling trying to provide a simpified example ended up in hiding real problem.
I have several applications within my domain that accept similar inputs in text fields. Each application implements its own validation. I want to bring that functionality into a class library so that rather than re-inventing the wheel on each project, our developers can quickly implement the validation library, and move on.
I'm not the best when it comes to OO design. What I need is the ability for a user to enter an arbitrary string, and then for the validation library to check it against the known types to make sure that it matches one of them. Should I build an interface and make each type of string a class that implements that interface? (seems wrong since I won't know the type when I read in the string). I could use some help identifying a pattern for this.
Thanks.
I've always been a fan of Fluent Validation for .Net. If it's more robust then you need, it's functionality is easy enough to mimic on your own.
If you're interested, here's a link to my very simple validation class. It's similar in usage to Fluent Validation, but uses lambdas to create the validation assertions. Here's a quick example of how to use it:
public class Person
{
public Person(int age){ Age = age; }
public int Age{ get; set;}
}
public class PersonValidator : AbstractValidator
{
public PersonValidator()
{
RuleFor(p => p.Age >= 0,
() => new ArgumentOutOfRangeException(
"Age must be greater than or equal to zero."
));
}
}
public class Example
{
void exampleUsage()
{
var john = new Person(28);
var jane = new Person(-29);
var personValidator = new PersonValidator();
var johnsResult = personValidator.Validate(john);
var janesResult = personValidator.Validate(jane);
displayResult(johnsResult);
displayResult(janesResult);
}
void displayResult(ValidationResult result)
{
if(!result.IsValid)
Console.WriteLine("Is valid");
else
Console.WriteLine(result.Exception.GetType());
}
}
(see source code for a more thorough example).
Output:
Is valid
System.ArgumentOutOfRangeException
Each application implements its own validation. I want to bring that functionality into a class library so that rather than re-inventing the wheel on each project, our developers can quickly implement the validation library, and move on.
Your problem seems similar to custom NUnit constraints.
NUnit allows something they call a constraint-based assertion model, and allow the user to create custom constraints, saying whether or not a given object satisfies the criteria of that constraint.
Using an object-based constraint model is superior to a purely function-based constraint model:
It lets you aggregate sub-constraints to evaluate a higher level constraint.
It lets you provide diagnostic information as to why a specific constraint doesn't match your input data.
This sounds fancy, but constraints are just functions that take a parameter of your desired type, returns true if it matches, and false if it doesn't.
Adapting it to your problem
What I need is the ability for a user to enter an arbitrary string, and then for the validation library to check it against the known types to make sure that it matches one of them.
You don't actually have to build assertions out of your constraints. You could evaluate constraints without throwing exceptions, and do your classifications first.
But I don't recommend you do any automatic classification. I recommend you attach a specific constraint to a specific input, rather than trying to match all available constraints. Pass in the string to that constraint, and call it done.
If you need to do this for higher level objects, build a constraint for the higher level object that uses specific (existing) constraints for each of its sub fields, as well as doing cross-field constraint validation.
When you're done, you can aggregate all constraint violations to the top level, and have your validation logic throw an exception containing all the violations.
BTW, I wouldn't use the exact same interface NUnit does:
It is a confusing design
I'd prefer an approach that used generics all the way through
I'd prefer an approach that allowed you to return an IEnumerable<ConstraintViolation> or IEnumerable<string>, rather than taking some sort of output writer class as a dependency
But I'd definitely steal the base concept :)
Implementation
Here's an example implementation of what I'm talking about:
public class ConstraintViolation
{
public ConstraintViolation(IConstraintBase source, string description)
{
Source = source;
Description = description;
}
public IConstraintBase Source { get; }
public string Description { get; set; }
}
public interface IConstraintBase
{
public string Name { get; }
public string Description { get; }
}
public interface IConstraint<T> : IConstraintBase
{
public IEnumerable<ConstraintViolation> GetViolations(T value);
}
And here's an example constraint to validate the length of a string (a weak example, but see my comments about this below):
public class StringLengthConstraint : IConstraint<string>
{
public StringLengthConstraint(int maximumLength)
: this(minimumLength: 0, maximumLength: maximumLength)
{
}
public StringLengthConstraint(int minimumLength, int maximumLength,
bool isNullAllowed = false)
{
MinimumLength = minimumLength;
MaximumLength = maximumLength;
IsNullAllowed = isNullAllowed;
}
public int MinimumLength { get; private set; }
public int MaximumLength { get; private set; }
public bool IsNullAllowed { get; private set; }
public IEnumerable<ConstraintViolation> GetViolations(string value)
{
if (value == null)
{
if (!IsNullAllowed)
{
yield return CreateViolation("Value cannot be null");
}
}
else
{
int length = value.Length;
if (length < MinimumLength)
{
yield return CreateViolation(
"Value is shorter than minimum length {0}",
MinimumLength);
}
if (length > MaximumLength)
{
yield return CreateViolation("Value is longer than maximum length {0}",
MaximumLength);
}
}
}
public string Name
{
get { return "String Length"; }
}
public string Description
{
get
{
return string.Format("Ensure a string is an acceptable length"
+ " - Minimum: {0}"
+ ", Maximum: {1}"
+ "{2}"
, MinimumLength
, MaximumLength
, IsNullAllowed ? "" : ", and is not null"
);
}
}
private ConstraintViolation CreateViolation(string description,
params object[] args)
{
return new ConstraintViolation(this, string.Format(description, args));
}
}
Here's how to use it when doing validation of a single field:
var violations = new StringLengthConstraint(10).GetViolations(value);
if(violations.Any())
{
throw new InvalidArgumentException("value", string.Join(", ", violations));
}
Justification
The string length constraint is a lot of code to do something stupidly simple, especially if you're doing this just once. But there are advantages to this approach:
It is reusable
Write this or use it once, and I'd agree this is a pain.
But most of the code here is to allow this to be reusable. For example you can select this out of a list of constraints for a string type. Or you can display a list of constraints or constraint violations on a UI, with tooltips, etc. Or you can use it in a unit testing framework; With an adapter class it could plug directly into NUnit.
This model supports aggregating constraints and violations
Through Linq
Through object composition
Linq:
var violations = new SomeConstraint(someData).GetViolations(value)
.Concat(new SomeOtherConstraint(someData).GetViolations(value))
;
Object composition:
// ...
public IEnumerable<ConstraintViolation> GetViolations(SomeType value)
{
if(value == 42)
{
yield return new ConstraintViolation(this, "Value cannot be 42");
}
foreach(var subViolation in subConstraint.GetViolations(value))
{
yield return subViolation;
}
}
private SomeSubConstraint subConstraint;
You need to do the following:
Parse your string and figure out (somehow) what type of string it is. I'd prefer to know it before the validation (by assigning types to fields), because if some string is incorrect, you can assign incorrect type for it.
Validate your string based on the validation rules applicable to the given field type. These validators should implement some interface, so you can validate any type of string. Usually you have not only field-type-specific validation, but field-specific-validation, so this kind of validators should also implement the same interface.
Everything else is depending on your app-specific logic.
I'm very new to C# (this is my first C# project). I'm fairly confident with the basics, but I'm starting to run into things that are raising issues that I can't quite solve, no matter how many different ways I Google it. A LOT of my questions have been answered by this site. :]
So, since I can't find the answer to this question, I decided to post it myself.
Maybe it's too basic of a question that everyone pretty much knows it, but I couldn't figure this out from the MSDN reading.
It has to do with C# Generics. I'm programming for a video game engine, and I've created a simple messaging system between AI units. The Message class contains members like sender, receiver, dispatchTime, and extraInfo. I want to use the extraInfo member to be a useful, flexible addition to the Message class, so I would like for it to be able to contain any type (an int node index, a double path cost, a relevant Vector3 position from XNA, etc, etc...). My research for this pointed me in the direction of Generics.
I figured out how to use Generics in something like a List, but I haven't read anything about how to just declare and implement a generic -member-. Just a single member, not a collection.
How would I declare this member, extraInfo? Additionally, when accessing it from another class, I would like to be able to type:
info = message.extraInfo;
..to retrieve the extra information via the get property.
How would this be done in C#?
Your message class would look something like this
public class Message<T>
{
public object Sender { get; set; }
public object Receiver { get; set; }
public T ExtraInfo { get; set; }
}
public static void Main()
{
Message<double> doubleMessage = new Message<double>() { ExtraInfo = 4.0d };
Message<string> stringMessage = new Message<string>() { ExtraInfo = "Hello World" };
}
Using .NET 4.0, you can make your ExtraInfo property of type dynamic. You could then store anything at all in it, and as long as you access it properly at runtime, you'll be ok.
You could declare the extraInfo member of your class as an object. You could then put anything you want in there.
You can solve your problem by creating an ExtraInfoType object that contains an
object as well as implicit operators to convert to and from the various object types transparently.
The ExtraInfoType object can also indicate what kind of object is stored in the ExtraInfoType.
An example of this implementation is below.
enum ExtraInfoKind
{
Integer,
Double
}
class ExtraInfoType
{
object value;
public object Value {
get { return value; }
}
ExtraInfoKind kind;
public ExtraInfoKind Kind {
get { return kind; }
}
private ExtraInfoType(object o, ExtraInfoKind kind){
this.value=o;
this.kind=kind;
}
public static implicit operator int(ExtraInfoType o){
if(o.kind!= ExtraInfoKind.Integer)
throw new InvalidCastException();
return (int)o.value;
}
public static implicit operator double(ExtraInfoType o){
if(o.kind!= ExtraInfoKind.Double)
throw new InvalidCastException();
return (double)o.value;
}
public static implicit operator ExtraInfoType(int o){
return new ExtraInfoType(o, ExtraInfoKind.Integer);
}
public static implicit operator ExtraInfoType(double o){
return new ExtraInfoType(o, ExtraInfoKind.Double);
}
}
/* Example
class Program
{
public static void Main(string[] args)
{
ExtraInfoType t=1;
Console.WriteLine(t.Kind);
int valueT=t;
Console.WriteLine(t);
Console.ReadLine();
}
}
*/
Here you would declare extraInfo under the type ExtraInfoType.
Note that no generics are necessary here. Note also that ExtraInfoType
can store only one kind of object, which can be determined by the Kind property.
If the object is cast to the wrong type, an InvalidCastException is thrown, as
can be seen in the implicit operators above.
Trying to decipher an appropriate OO design to implement. The basic scenario is that you have a PstnNumber which is essentially a 10 digit phone number that always starts with 0 (e.g. 0195550000). A rule has been introduced to allow auto-correcting of a number if the leading 0 is missing (e.g. 195550000).
START EDIT
I realised the original question may have been misunderstood (thankyou kindly to those whom have answered already), so I have edited to try and better explain the scenario.
END EDIT
I started playing with some preliminary concepts and then thought I would ask if there was a more appropriate way to go or do one of these suffice (on some level)?
Concept 1
public class PstnNumber
{
public virtual string Number { get; set; }
public PstnNumber() { }
public PstnNumber(string number)
{
this.Number = number;
}
}
public class AutoFormattedPstnNumber : PstnNumber
{
public override string Number
{
get { return base.Number; }
set { base.Number = value.PadLeft(10, '0'); }
}
public AutoFormattedPstnNumber() : base() { }
public AutoFormattedPstnNumber(string number)
{
this.Number = number;
}
}
Concept 2 (removed)
Concept 3
public class PstnNumber
{
public bool AutoCorrect { get; set; }
private string number;
public virtual string Number
{
get { return (this.AutoCorrect) ? this.number.PadLeft(10, '0') : this.number; }
set { this.number = value; }
}
public PstnNumber() : this(false) { }
public PstnNumber(bool autoCorrect)
{
this.AutoCorrect = autoCorrect;
}
public PstnNumber(string number) : this(false)
{
this.Number = number;
}
public PstnNumber(string number, bool autoCorrect) : this(autoCorrect)
{
this.Number = number;
}
}
I think Concept 1 may violate the Liskov Substitution rule because the subclass changes the behaviour of the Number property (happy to learn if I've misunderstood that).
Any alternative suggestions would be received happily.
do you have to do the autoformatting when the object is instantiated? If not,
what about:
public class PstnNumber
{
public virtual string Number { get; set; }
public PstnNumber() { }
public PstnNumber(string number) { this.Number = number; }
public AutoFormatNumber { get { return Numer.PadLeft(10, '0'); } }
}
avoid getter-setter-surprise
Avoid getters returning a different value than the one accepted by the setter. Imagine the following snippet:
if (input.Value != current.Number)
{
NumberChangedAgain = true;
current.Number = input.Value;
}
A simple solution would be to make PstnNumber immutable:
temp = PstnNumber.FromString(input.Value);
if (temp != current) { ... }
canonical format
If some data has different representations, there is a lot of advantage to storing it in a canonical representation, and move the format conversions to factory functions and getters / formatters. For example, you don't need to test comparison for short vs. long, long vs. short, short vs. short, long vs. long.
different aspects
Do you need the distinction between an "autoformatted" and a "normal" number, or is this merely a question of input and output - i.e.
does display format (short or long) depend on how the number was entered, or on where it is displayed?
is 0195550000 == 195550000 ?
I'd prefer to fold both classes into one if possible (i.e. when "entered with or without 0 can be forgotten"):
public class PstnNumber
{
private string m_number; // always in long format
public static PstnNumber(string s) { ... } // accepts short and long form
public string Number { get { return m_number; } }
public string AutoFormatted { { get { ... } }
}
Otherwise I'd go with Option 3, but always store the long format in m_number.
In Option 1 and Option 2, you aren't preserving the original number anyway, rendering the subclass worthless (except to know that it was autoformatted at some point, which doesn't seem like useful information). The alternative to make these Options more useful would be to format on Get instead of Set.
Option 3 is therefore the preferred pattern out of these three options, but I would also ask - why can't the PstnNumber also simply detect the number of digits, and autoformat accordingly?
If you follow the rules - there is one that says that "each routine (read class) should do only one thing and do it well".
According to that I would make PstnNumber just hold the number, and create some sort of factory that produces the right number.
Doing both in the same class means that you are weaving domain logic and representation. I prefer them separated.
I'd ask why your class name is so cryptic. "Number" is clear to me, and "P" suggests "phone", but what's the "stn" telling me? A few extra keystrokes would make this class more self-documenting.
I'd also ask about the logic of a default constructor that does not initialize the underlying data members to some value. I think a default constructor should have a sensible default value if possible.
I feel like option 1 is overkill. I don't think inheritance is making this model clearer or better. I don't see how it breaks Liskov substitution, which demands that you can use the subclass in any situation that calls for a base class. The methods map 1:1 as far as I can see. How is Liskov violated?
Option 2 says these are two separate classes with no relationship. That doesn't seem right to me.
All this work suggests that your problem will require that you use both classes. You'll have situations where the leading zero is NOT required and others where it is. Is that true? Or are you always going to require the leading zero?
I don't care for any of your options. I'd prefer an interface or a static factory or even modifying the class you have to anything you've suggested. It feels like a mere formatting issue. Do you store the number with the leading zero? If not, maybe it's just a view concern.
Do you have a really strong reason to have a setter and not have your members final? If not, that's probably a bigger problem than any other variation between the three.
So I'd go for a stateless #3 which means making the number final and gettng rid of the autoFormat variable.
For simplicity I'd just have a getNumberRaw and getNumberFormatted
Better yet, you could have getNumberRaw and getNumber(formatType) where formatType actually contains the code that formats the number since the format may change again in the future and combining formatting (view) with your phone number (model) isn't optimal.
(PS/EDIT): just the fact that a phone number can change is NOT a good reason to have a setter! Creating a new phone number object and replacing the old one will almost always work!
I am not familiar with c#, but I'd do this:
public class PstnNumber {
readonly string number;
public PstnNumber(string number) {
this.number = number;
}
public string getNumber() {
return number;
}
static public PstnNumber createNumber(string number) {
return new PstnNumber(number.PadLeft(10, '0'));
}
}
Of course if I knew how Properties work, I'd probably do it differently :)
I would go with a much simpler version, overriding the ToString method, or even, creating an ToString overload that receives the bool parameter indicating that the number should be formatted.