We went through structs at class and we've been tasked with creating a method inside the struct that changes the fields of an instance compared with another. For example, changing one's name if the other is a manager.
struct Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get => FirstName + LastName;
}
public int Age { get; set; }
public string Role { get; set; }
public double Salary { get; set; }
public bool IsManager { get; set; }
public static void EditInfo(ref Employee e1, ref Employee e2)
{
if (e1.IsManager)
{
Console.WriteLine($"Feel free to set {e2.FirstName}'s info: ");
e2.FirstName = Console.ReadLine();
e2.LastName = Console.ReadLine();
Console.WriteLine($"From now on thy name is {e2.FullName}!");
}
else if (e2.IsManager)
{
Console.WriteLine($"Feel free to set {e1.FirstName}'s info: ");
e1.FirstName = Console.ReadLine();
e1.LastName = Console.ReadLine();
Console.WriteLine($"From now on thy name is {e1.FullName}!");
}
}
}
Now, I have this gut feeling that my creation is terrible. I'm not sure about how to actually approach this at all, and it looks like a valuable lesson to learn for real business applications. I would appreciate every tip and trick that is necessary to make these methods, that interact with multiple instances of the struct, better.
Edit: Clarification
First you should make the mutable properties private set;, the constructor accept initial state, and make EditInfo an instance method. This more or less treats each Employee like a database record with access control, creating a simpler relationship.
public class Employee
{
public Employee(string firstName, string lastName, bool isManager, ...)
{
FirstName = firstName;
LastName = lastName;
IsManager = isManager;
// remaining properties...
}
public string FirstName { get; private set; }
public string LastName { get; private set; }
public bool IsManager { get; private set; }
// remaining properties...
public void Edit(Employee requester)
{
if (requester.IsManager) {
// query input from the user, make changes, and notify the user.
} else {
// notify the user that he doesn't have permission to edit this employee.
}
}
}
Using it thus becomes:
var manager = new Employee("Your", "Boss", isManager: true);
var sheep = new Employee("John", "Doe", isManager: false);
sheep.Edit(manager); // Essentially says "edit sheep using manager as permission".
Secondly, unless your instructor forbade you from using class, you should make it a class and get rid of all the ref declarations. struct and ref are very useful in the right cirumstances, but unless you're aware of the tradeoffs and code rigorously, its too easy to introduce a bug that will be difficult to reason about considering your experience level.
Classes don't automatically make copies or require the ref keyword. Structs on the other hand do make copies without ref, thus semantically requiring(but not enforcing) it at every call site that makes modifications to it. This has further implications which you may not be aware of. Consider properties. Properties cannot be passed with ref, only locals, fields, and array elements. Unfortunately that means it's currently impossible to pass a struct property by ref(though subject to change in a future C# version). This will likely bite you down the road.
In summary, prefer classes in C# unless you have a good reason to use structs(i.e. GC performance, very special purpose types). Above all, aim for readability.
Related
A user inserts KID NAME, the code instantiate an object and add it to list of objects.
How to give each object a unique number automatically starting at 1, such that first KID(object) gets number 1, second KID gets number 2 etc.
I tried something like this but the result is "stackoverflow".
How would the class should be?
class Kid
{
public string KidName{ get; set; }
private static int Number { get; set; }
public int KidNumber { get; set; }
public Kid (string name)
{
this.KidName = name;
Number++;
this.KidNumber = Number;
}
}
Your code works successfully on my device.
Maybe you have an 'stackoverflow' for other reasons?
If you need only to identify Kid class, the alternative way is to use Guid:
class Kid
{
public string KidName { get; set; }
public Guid KidNumber { get; set; }
public Kid(string name)
{
this.KidName = name;
this.KidNumber = Guid.NewGuid();
}
}
Nothing in your example can induce a StackOverflowException. Stackoverflows occur when you repeatedly call the same method recursively either directly or indirectly (by way of an intermediate) or when you have an instance-level field of the same type as the instance itself (a special case of the former). You don't exhibit either case in your example so it must be due to something else external to the code you've shown. The most your code could potentially exhibit is an OverflowException if you were in a checked context.
That said, your approach is OK at first glance with the obvious pitfall that it is not thread-safe. Two instance could in theory end up with the same KidNumber. You can fix that by making use of the Interlocked class:
using System.Threading;
class Kid
{
private static int _counter;
public string KidName { get; set; }
public int KidNumber { get; set; }
public Kid(string name)
{
this.KidName = name;
this.KidNumber = Interlocked.Increment(ref _counter);
}
}
Like ++ in an unchecked context, this handles the case of overflow; once you hit int.MaxValue, if will automatically wrap to int.MinValue. If you don't want negative IDs you'll have to handle that seperately.
This is probably a very beginner question but I have searched a lot of topics and couldn't really find the same situation, although I'm sure this kind of situation happens all the time.
My project/program is going to track changes to drawings on construction projects and send notifications to people when drawings are changed.
There will be many construction projects (job sites), which will in turn have many drawings in each one. Each drawing will have a couple of revisions (as they get changed, a new revision is created).
Here is my Project Class
public class Project
{
private readonly List<Drawing> _drawings = new List<Drawing>(30);
private readonly List<Person> _autoRecepients = new List<Person>(30);
public int ID { get; private set; }
public string ProjectNumber { get; private set; }
public string Name { get; private set; }
public bool Archived { get; private set; }
public List<Person> AutoRecepients { get { return _autoRecepients; } }
public Project(int id, string projectNumber, string name)
{
if (id < 1) { id = -1; }
ID = id;
ProjectNumber = projectNumber;
Name = name;
}
public bool AddDrawing(Drawing drawing)
{
if (drawing == null) return false;
if (_drawings.Contains(drawing)) { return true; }
_drawings.Add(drawing);
return _drawings.Contains(drawing);
}
public void Archive()
{
Archived = true;
}
public bool DeleteDrawing(Drawing drawing)
{
return _drawings.Remove(drawing);
}
public IEnumerable<Drawing> ListDrawings()
{
return _drawings.AsReadOnly();
}
public override string ToString()
{
return string.Format("{0} {1}", ProjectNumber, Name);
}
}
Here is my Drawing Class
public class Drawing : IDrawing
{
private List<IRevision> _revisions = new List<IRevision>(5);
private List<IssueRecord> _issueRecords = new List<IssueRecord>(30);
private IRevision _currentRevision;
public int ID { get; private set; }
public string Name { get; private set; }
public string Description { get; set; }
public Project Project { get; private set; }
public IRevision CurrentRevision { get { return _currentRevision; } }
public Drawing(int id, string name, string description, Project project)
{
// To be implemented
}
/// <summary>
/// Automatically issue the current revision to all Auto Recepients
/// </summary>
public void AutoIssue(DateTime date)
{
AutoIssue(date, _currentRevision);
}
/// <summary>
/// Automatically issue a particular revision to all Auto Recepients
/// </summary>
public void AutoIssue(DateTime date, IRevision revision)
{
}
public void IssueTo(Person person, DateTime date, IRevision revision)
{
_issueRecords.Add(new IssueRecord(date, this, revision, person));
throw new NotImplementedException();
}
public void IssueTo(Person person, DateTime date)
{
IssueTo(person, date, _currentRevision);
}
public void IssueTo(IEnumerable<Person> people, DateTime date)
{
IssueTo(people, date, _currentRevision);
}
public void IssueTo(IEnumerable<Person> people, DateTime date, IRevision revision)
{
foreach (var person in people)
{
IssueTo(person, date, revision);
}
}
public void Rename(string name)
{
if (string.IsNullOrWhiteSpace(name)) { return; }
Name = name;
}
public void Revise(IRevision revision)
{
if (revision.Name == null ) return;
_revisions.Add(revision);
_currentRevision = revision;
}
public struct IssueRecord
{
public int ID { get; private set; }
public DateTime Date { get; private set; }
public IDrawing Drawing { get; private set; }
public IRevision Revision { get; private set; }
public Person Person { get; private set; }
public IssueRecord(int id, DateTime date, IDrawing drawing, IRevision revision, Person person)
{
if (id < 1) { id = -1; }
ID = id;
Date = date;
Drawing = drawing;
Revision = revision;
Person = person;
}
}
}
And here is the Revision struct
public struct Revision : IRevision
{
public int ID { get; private set; }
public string Name { get; }
public DateTime Date { get; set; }
public IDrawing Drawing { get; }
public IDrawingFile DrawingFile { get; private set; }
public Revision(int id, string name, IDrawing drawing, DateTime date, IDrawingFile drawingFile)
{
if (name == null) { throw new ArgumentNullException("name", "Cannot create a revision with a null name"); }
if (drawing == null) { throw new ArgumentNullException("drawing", "Cannot create a revision with a null drawing"); }
if (id < 1) { id = -1; }
ID = id;
Name = name;
Drawing = drawing;
Date = date;
DrawingFile = drawingFile;
}
public Revision(string name, IDrawing drawing, DateTime date, IDrawingFile drawingFile)
: this(-1, name, drawing, date, drawingFile)
{
}
public Revision(string name, IDrawing drawing)
: this(-1, name, drawing, DateTime.Today, null)
{
}
public void ChangeID(int id)
{
if (id < 1) { id = -1; }
ID = id;
}
public void SetDrawingFile(IDrawingFile drawingFile)
{
DrawingFile = drawingFile;
}
}
My question is to do with the project reference in the drawing class and the drawing reference in the revision struct.
It seems like a bit of a code smell?
It also seems like it may cause issues with serialization in the future.
Is there a better way to do this?
It seems necessary for a drawing object to know what project it belongs to so that if I'm working with individual drawing objects I can know which project they belong to.
Similarly, each revision is essentially "owned" by or part of a drawing. A revision doesn't make sense without a drawing so it needs a reference to the drawing it belongs to?
Any advise would be much appreciated.
What you have are not so much circular references as two examples of
a parent-child relationship which is navigable from both ends.
Yes it is normal and acceptable and no it isn't a code smell. Yes, some serialization tools require you to hint. e.g. Newtonsoft.Json would want the ReferenceLoopHandling.Ignore setting.
Navigability as a concept is not always talked about in OO design, which is unfortunate because it's just the concept you want here. (It is an explicit term in UML).
You often don't need navigability from both ends. Parent-child relationships are often coded from parent to child only. This is really common. For instance, an invoiceline class rarely needs an explicit field for its parent invoice because most applications only ever look at the line after retrieving the parent invoice.
So the design decision is not,
"Does a revison make sense without a drawing?"
But
"Will I ever need to find a drawing given only a revision?"
My guess is that your revisions are like invoice lines, and don't need to navigate to their parent. The answer for the drawings <——> project relation is not obvious to me. (It is an analysis question about your domain, not a question about coding style).
There is a striking difference here between OO code and, for instance, SQL. In a SQL database, it must be the revision table that holds the reference to its parent drawing id. In OO code, the parent class nearly-always holds a reference to the children. The children often don't need a reference to their parent because the only way you access the children is by already having the parent.
Circular references are quite normal in C# programs and data models in general, so don't worry about them. They do have to be specially handled during serialization though.
Yes, it's a circular reference and yes it's a code smell. Furthermore I do think the smell is right in this instance, it is not a good OO design.
Disclaimers
It might be normal for C# programs as #Rugbrød puts it, I can't comment on that, I'm not a C# coder.
This kind of design might be ok for non-oo paradigms, such as "component-based" or procedural programming.
So you can ignore this smell I guess if this is the context of your code.
Details
The main problem is that you are modeling data, not behavior. You want to have the "data" right first, and then you will go on to think about actual functions you want to implement on top of that. Like displaying drawings, archiving, etc. You don't have those yet, but that is in your mind right?
The OO approach (as admittedly not everyone agrees with) is to model behavior. If you want your drawings archived, then implement Drawing.Archive(). And I don't mean setting a flag, I mean really copying it to cold-storage or whatever. The real business-function your application is supposed to do.
If you do that, then you will find, there are no behaviors that mutually need each other, since that is then one behavior obviously. What can happen is, that two behaviors need a third abstract one perhaps (sometimes called Dependency Inversion).
I think the only problem here is Drawing.CurrentRevision
Otherwise, a Revision belongs to a Drawing, which belongs to a Project.
CurrentRevision isn't really a property of the Drawing, it's a shortcut to one item in its 'Revisions' list.
How about changing it to a method GetCurrentRevision() and a CurrentRevisionID property? That way it's obvious that GetCurrentRevision should not be serialised, although the ID is.
When refactoring code, I come up with instances like the following
private string _property = string.Empty;
public string Property
{
set { _property = value ?? string.Empty); }
}
Later on in a method I see the following:
if (_property != null)
{
//...
}
Assuming that _property is only set by the setter of Property, is this code redundant?
I.e is there any way, through reflection wizardry or other methods that _property can ever be null?
Assuming that _property is only set by the setter of Property, is this
code redundant?
Exactly, it is redundant. This is the actual purpose of Properties. We shouldn't access the fields of a class directly. We should access them using a Property. So in the corresponding setter, we can embed any logic and we can rest assure that each time we try to set a value this logic would be verified once more.This argument holds even for the methods of a class. In a method we must use the properties and not the actual fields. Furthermore, when we want to read the value of a field, we should make use of the corresponding getter.
In general, properties enhances the concept of encapsulation, which is one of the pillars of object oriented programming OOP.
Many times there isn't any logic that should be applied when we want to set a value. Take for instance the following example:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
We have declared a class for representing a Customer. A Customer object should have three properties an Id, a FirstName and a LastName.
An immediate question, when someones read this class is why should someone make use of properties here?
The answer is again the same, they provide a mechanism of encapsulation. But let's consider how can this help us in the long run. Let's say that one day someone decides that the first name of a customer should be a string of length less than 20. If the above class had been declared as below:
public class Customer
{
public int Id;
public string FirstName;
public string LastName;
}
then we should check for the length of FirstName in each instance we had created ! Otherwise, if we had picked the declaration with the properties, we could just easily make use of Data Annotations
public class Customer
{
public int Id { get; set; }
[StringLength(20)]
public string FirstName { get; set; }
public string LastName { get; set; }
}
and that's it. Another approach it could be the following:
public class Customer
{
public int Id { get; set; }
private string firstName;
public string FirstName
{
get { return firstName }
set
{
if(value!=null && value.length<20)
{
firstName = value;
}
else
{
throw new ArgumentException("The first name must have at maxium 20 characters", "value");
}
}
}
public string LastName { get; set; }
}
Consider both of the above approaches with having to revisit all your codebase and make this check. It's crystal clear that properties win.
Yes, it is possible through reflection. Nevertheless, I wouldn't worry about reflection -- people using reflection to defeat the design of your class is not something I worry about.
There is, however, something I do worry about: the phrase "Assuming that _property is only set by the setter of Property" is key. You are preventing users of your class from setting property to null.
You do not prevent, however, yourself or some other maintainer of your class from forgetting to only use the property INSIDE your class. In fact, your example has some one checking the field from inside the class rather than the property itself.... which means that, within your class, access comes from both the field and the property.
In most cases (where the problem could only come from inside the class) I would use an assertion and assert the field is not null.
If I really, really, really wanted to make sure that it wasn't null (barring reflection or people hell-bent on breaking things), you could try something like this:
internal class Program
{
static void Main()
{
string example = "Spencer the Cat";
UsesNeverNull neverNullUser = new UsesNeverNull(example);
Console.WriteLine(neverNullUser.TheString);
neverNullUser.TheString = null;
Debug.Assert(neverNullUser.TheString != null);
Console.WriteLine(neverNullUser.TheString);
neverNullUser.TheString = "Maximus the Bird";
Console.WriteLine(neverNullUser.TheString);
}
}
public class UsesNeverNull
{
public string TheString
{
get { return _stringValue.Value; }
set { _stringValue.Value = value; }
}
public UsesNeverNull(string s)
{
TheString = s;
}
private readonly NeverNull<string> _stringValue = new NeverNull<string>(string.Empty, str => str ?? string.Empty);
}
public class NeverNull<T> where T : class
{
public NeverNull(T initialValue, Func<T, T> nullProtector)
{
if (nullProtector == null)
{
var ex = new ArgumentNullException(nameof(nullProtector));
throw ex;
}
_value = nullProtector(initialValue);
_nullProtector = nullProtector;
}
public T Value
{
get { return _nullProtector(_value); }
set { _value = _nullProtector(value); }
}
private T _value;
private readonly Func<T, T> _nullProtector;
}
It is basically redundant. However, if it were mission critical or if for some reason it caused terrible side effects, it could remain. It is hard to tell, but part of your question was "can reflection change this value to null" to which the answer is yes and can be seen here in this linqpad demo
void Main()
{
var test = new Test();
test.Property = "5";
Console.WriteLine(test.Property);//5
FieldInfo fieldInfo = test.GetType().GetField("_property",BindingFlags.NonPublic | BindingFlags.Instance);
fieldInfo.SetValue(test, null);
Console.WriteLine(test.Property);//null
}
public class Test
{
private string _property = string.Empty;
public string Property
{
get { return _property; }
set { _property = value ?? string.Empty; }
}
}
I know this question is old, but look, I needed that one of my string properties never came up in null.
So I did this, and It worked for me
public string Operation { get; set; } = string.Empty;
In this way the default value is a string empty, but never null.
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 9 years ago.
I was thinking about language constructs and how when we talk about classes and objects in Object oriented languages we draw comparisons to real world. Like when people talk of Inheritance people would quote an example of Parent and Children. One thing that i don't find in OO languages that i know , mainly C, C++, C#, is that they don't have a mechanism to declare a property as mandatory. What i mean by that is I cannot define a class called human and say that face, hands and lets say eye are mandatory property of my class. By having that construct i can enforce that anyone who is using my class need to set those properties before using my class. If user forgets to set those properties then i should get an compile time error.
Just wanted to see community thoughts on that.
Here is reason why i had asked above question:
When i build my user controls, i want to make sure that users should set some of the properties in their code when they use my control. For example, lets say i build a customer user control that would be used by other developers in my team. Some of the properties that i have exposed are: "CustomerId", "FirstName", "LastName", "Address1", "City", "State" and ZipCode. Now i want to make sure that any consumer of my control should set "CustomerId". Using Constructor to enforce that the value is set is a way but it will throw a run time exception plus how would user call that constructor from .cs file without dynamically creating the control and adding it to control collection.
You can do that, with a DDD principle: create a class with a private default constructor, and a public constructor that accept required parameters and validate its values. If a value is invalid, throw an exception so that the object cannot be created. Properties could also have private setters instead of public setters.
You can also create a 'Mandatory' attribute and put those on top of the properties that are mandatory; and have a mechanism that checks this based on whether a property has been decorated with the attribute or not.
Example:
public class BlogEntry
{
private BlogEntry() {}
public BlogEntry(string title, string body)
{
LastModifiedDate = DateTime.Now;
Title = title;
Body = body;
var blogEntryValidator = new BlogEntryValidator();
blogEntryValidator.ValidateAndThrow(this);
}
public int Id { get; private set; }
public string Title { get; private set; }
public string Body { get; private set; }
public DateTime? LastPublishDate { get; private set; }
public DateTime LastModifiedDate { get; private set; }
public virtual ICollection<Comment> Comments { get; private set; }
public void Publish()
{
LastPublishDate = DateTime.Now;
}
public void Unpublish()
{
LastPublishDate = null;
}
public void Modify(string title, string body)
{
Title = title;
Body = body;
LastModifiedDate = DateTime.Now;
}
public Comment AddComment(string commentText, string emailAddress, string name)
{
var comment = new Comment(this, commentText, emailAddress, name);
if (Comments == null) Comments = new List<Comment>();
Comments.Add(comment);
return comment;
}
public void RemoveComment(Comment comment)
{
Comments.Remove(comment);
}
}
public class Comment
{
private Comment() {}
public Comment(BlogEntry blogEntry, string name, string emailAddress, string commentText)
{
BlogEntry = blogEntry;
Name = name;
EmailAddress = emailAddress;
CommentText = commentText;
DateWritten = DateTime.Now;
var commentValidator = new CommentValidator();
commentValidator.ValidateAndThrow(this);
}
public int Id { get; private set; }
public string Name { get; private set; }
public string EmailAddress { get; private set; }
public string CommentText { get; private set; }
public DateTime DateWritten { get; private set; }
public BlogEntry BlogEntry { get; private set; }
}
Yes, C++ and C# allow for this via constructors.
class A
{
public:
A(int x, int y, int z)
: _x(x_, _y(y), _z(z) {}
private:
int _x;
int _y;
int _z;
};
You cannot create an instance of A without providing values for _x, _y, and _z.
The reason is that state needed to fulfil class invariants should be provided during object construction, so you should provide values of 'mandatory' properties as constructor parameters. Your question is based on false assumption that an object is characterized by setting state with properties. This is wrong for a handful of reasons, some of which are:
many, if not most OO languages have no properties: Java, C++,...
what you use is only formally an object, it is actually a plain record and it is not very object oriented, same as e.g. C++ struct without methods (see notes at the bottom about setters vs methods)
Allowing the client to create instances of the objects which are only later set up with correct values for mandatory state is sure-fire way to spend many hours in company of debugger.
Let's take some User with invariant that first and last name must always be set.
class User {
public User(string first, string last) { ... }
public User(string first, string last, uint age) : this(first, last) { ... }
}
// client code:
var user = new User("john", "doe");
var user2 = new User("Clint", "Eastwood", 82);
Compiler ensures that no one can instantiate the object without fulfilling the invariants.
Now compare it with your approach:
class User {
public User(string first, string last) { ... }
public User(uint age) { ... }
[Mandatory] public string FirstName { get; set; }
[Mandatory] public string LastName { get; set; }
}
// client code:
var actor = new User(82); // << invalid
actor.FirstName = "Clint";
actor.LastName = "Eastwood"; // << valid
This approach results in more code and allows for a period of time (between << invalid and << valid) where your object is not in a valid state. What if some of property setters throw an exception? You are left with broken object instance floating around. Do you expect the compiler to also verify that code in the setter can not throw? Do you think it is even possible? Besides that, every client which instantiates User instances must check what are the mandatory properties and make sure to set all of them. This effectively breaks encapsulation.
IMO, property setters should be rare, unlike getters. I believe that in such a class you should not have setters for FirstName/LastName, only getters. Instead there should be a method SetName(string first, string last) if you really want to allow name changing. Here's why:
// lets rename actor
actor.FirstName = "John";
actor.LastName = "Wayne";
If the last line throws, you are left with John Eastwood, an actor I have never heard about. With actor.SetName("John", "Wayne") this can't happen.
Additionally, what about property which have dependency in order you specify them, e.g.
obj.ErrorCode = 123; // imagine that error code must be != 0
obj.ErrorMsg = "foo"; // in order to be allowed to set error code
Would you also introduce attributes for that instead of having obj.SetErrorInfo(123, "foo")? This makes it obvious that properties break encapsulation as the order is caused by the implementation detail, unlike with method call.
Quite often, in languages like C#, required state or dependencies is provided in constructor while optional state can be set through properties. However, it is not properties or inheritance which make a language object-oriented.
Sure you can! Just use parameters in constructor to denote which are mandatory.
public class Human
{
public Face Face { get; set; }
public Hand Hand { get; set; }
public Human(Face face, Hand hand) {} etc...
}
In this instance, you cannot use the private constructor, so these properties are essentially "mandatory" in order to use the Human class.
I'm trying to build a class which will initalise its self either by passing in a reference to a record in a database (with the intention that a query will be run against the database and the returned values for the objects properties will be set therein), or by specifying the values "by hand" - this no database call is required.
I've looked at a couple textbooks to discover the "Best-practice" for this functionality and sadly I've come up short.
I've written a couple sample console apps reflecting what I believe to be the two most probable solutions, but I've no Idea which is the best to implement properly?
Sample App #1 uses what most books tell me is the "preferred" way but most examples given alongside those claims do not really fit the context of my situation. I'm worried in here that the flow is not as readable as App #2.
using System;
namespace TestApp
{
public class Program
{
public static void Main(string[] args)
{
var one = new OverloadedClassTester();
var two = new OverloadedClassTester(42);
var three = new OverloadedClassTester(69, "Mike", 24);
Console.WriteLine("{0}{1}{2}", one, two, three);
Console.ReadKey();
}
}
public class OverloadedClassTester
{
public int ID { get; set; }
public string Name { get; set; }
public int age { get; set; }
public OverloadedClassTester() : this(0) { }
public OverloadedClassTester (int _ID) : this (_ID, "", 0)
{
this.age = 14; // Pretend that this was sourced from a database
this.Name = "Steve"; // Pretend that this was sourced from a database
}
public OverloadedClassTester(int _ID, string _Name, int _age)
{
this.ID = _ID;
this.Name = _Name;
this.age = _age;
}
public override string ToString()
{
return String.Format("ID: {0}\nName: {1}\nAge: {2}\n\n", this.ID, this.Name, this.age);
}
}
}
This Sample (App #2) "appears" more readable - in that I think it's easier to see the flow of operation. However it does appear efficient in terms of characters saved :p. Also, is it not dangerous that it calls a method of the object before it's fully initialised, or is this not a concern?
using System;
namespace TestApp
{
public class Program
{
public static void Main(string[] args)
{
var one = new OverloadedClassTester();
var two = new OverloadedClassTester(42);
var three = new OverloadedClassTester(69, "Mike", 24);
Console.WriteLine("{0}{1}{2}", one, two, three);
Console.ReadKey();
}
}
public class OverloadedClassTester
{
public int ID { get; set; }
public string Name { get; set; }
public int age { get; set; }
public OverloadedClassTester()
{
initialise(0, "", 21); // use defaults.
}
public OverloadedClassTester (int _ID)
{
var age = 14; // Pretend that this was sourced from a database (from _ID)
var Name = "Steve"; // Pretend that this was sourced from a database (from _ID)
initialise(_ID, Name, age);
}
public OverloadedClassTester(int _ID, string _Name, int _age)
{
initialise(_ID, _Name, _age);
}
public void initialise(int _ID, string _Name, int _age)
{
this.ID = _ID;
this.Name = _Name;
this.age = _age;
}
public override string ToString()
{
return String.Format("ID: {0}\nName: {1}\nAge: {2}\n\n", this.ID, this.Name, this.age);
}
}
}
What is the "correct" way to solve this problem, and why?
Thanks,
I would definitely chain the constructors, so that only one of them does the "real work". That means you only have to do the real work in one place, so if that work changes (e.g. you need to call some validation method at the end of the constructor) you only have one place where you need to change the code.
Making "simple" overloads call overloads with more parameters is a pretty common pattern IME. I find it more readable than the second version, because you can easily tell that calling one overload is going to be the same as calling the "bigger" one using the default values. With the second version, you have to compare the bodies of the constructors.
I try not to have more than one constructor which chains directly to the base class wherever possible - unless it's chaining to a different base class constructor, of course (as is typical with exceptions).
Do not use database calls in a constructor. This means your constructor is doing a lot of work. See http://misko.hevery.com/code-reviewers-guide/ (Google guide for writing testable code).
Apart from this, chaining constructors (option 2) looks good. Mostly because as you say it is readable. But why are you assigning this.Name etc in the constructor and doing it again in initialize. You could assign all values in initialize.
Maybe something like this?
public class OverloadedClassTester
{
public int Id { get; private set; }
public string Name { get; private set; }
public int Age { get; private set; }
public OverloadedClassTester (Person person)
: this (person.Id, person.Name, person.Age) { }
public OverloadedClassTester(int id, string name, int age)
{
Id = id;
Name = name;
Age = age;
}
public override string ToString()
{
return String.Format("Id: {0}\nName: {1}\nAge: {2}\n\n",
Id, Name, Age);
}
}
maybe it would be better to use optional parameters? In this case, you would create a single constructor and initialize the values to the parameters you wish to set.
More information: link text
I prefer #1, the chaining constructors, from a maintenance perspective. When someone comes back to edit the code later on, you wouldn't want them to edit the initialise() method and not realize that it is being called from a constructor. I think it is more intuitive to have all the functional parts in a constructor of some kind.
Personally, I use constructor chaining like that all the time.