Is there a way to define my class so that callers of that class would get a compile time error unless they specified every property of the class, with the additional constraint that the parameters pass be named?
Let's say I have this interface:
public interface IPerson
{
string FirstName {get;}
string MiddleName { get; }
string LastName { get;}
}
And these classes:
public class PersonNeedAllProperties : IPerson
{
public string FirstName { get; private set;} // ideally readonly
public string MiddleName { get; private set;}
public string LastName { get; private set;}
public PersonNeedAllProperties(
string firstName,
string lastName,
string middleName)
{
this.FirstName = firstName;
this.MiddleName = middleName;
this.LastName = lastName;
}
}
public class PersonCanNamePropertiesButNotSadlyNotRequired : IPerson
{
public string FirstName { get; set;} // ideally readonly
public string MiddleName { get; set;}
public string LastName { get; set;}
}
Then the problems with these imho are the following:
var p1 = new PersonNeedAllProperties("lastName", "firstName", "middle");
// woops wrong order because not named.
var p2 = new PersonCanNamePropertiesButNotSadlyNotRequired()
{
FirstName = "firstName",
LastName = "lastName"
}; // woops forgot all about the middlename.
Is there not a way to get a simple POCO object with an initalization similar to person two but that requires that all the properties be named?
Here is the fiddle of the above: https://dotnetfiddle.net/gUk8eT#
To answer your question No, this is not possible.
You ask to be able to force two things:
a) force all properties be set
b) force properties to be named.
These things can be done on their own but they cannot be combined because the methods for doing so are diametrically opposed as described below.
a) To force all properties be set you need to take them as inputs in a constructor.
b) To force properties to be set by name they cannot be specified in the constructor.
Side note: It is possible to provide named parameters to a constructor but it is not possible to force these i.e var person = new Person(firstName: 'John', lastName: 'Doe', middleName: 'A')
The closest you can get is to mark your properties readonly (private set) and have them only be set from within your constructor as you have done in your PersonNeedAllProperties class.
Something that might be a viable alternative is called the Builder Pattern.
This will have an extra class that is responsible for constructing your object, but you still wouldn't be able to get a compile time error, only runtime.
There's a horrible horrible way to almost enforce this. Not by name but by type. i'm not recommending it. but there is a horrible way to do it. did i mention it is horrible?
"any problem in computer science can be solved by adding another layer of indirection" - Wheeler?
Make each of the properties their own distinct type. now you can't pass them in the wrong order or the compiler will tell you that you passed an invalid type.
public class FirstName
{
string Value { get; set; }
}
public class MiddleName
{
string Value { get; set; }
}
public class LastName
{
string Value { get; set; }
}
public interface IPerson
{
FirstName FirstName {get;}
MiddleName MiddleName { get; }
LastName LastName { get;}
}
public class PersonNeedAllProperties : IPerson
{
public FirstName FirstName { get; private set;} // ideally readonly
public MiddleName MiddleName { get; private set;}
public LastName LastName { get; private set;}
public PersonNeedAllProperties(
FirstName firstName,
MiddleName lastName,
LastName middleName)
{
this.FirstName = firstName;
this.MiddleName = middleName;
this.LastName = lastName;
}
}
This can be achieved with a nested class I guess:
public interface IPerson
{
string FirstName { get; }
string MiddleName { get; }
string LastName { get; }
}
public class Person : IPerson
{
public string FirstName { get; private set; }
public string MiddleName { get; private set; }
public string LastName { get; private set; }
//Make sure the parameterless constructor is private
private Person() { }
private Person(string first, string middle, string last)
{
this.FirstName = first;
this.MiddleName = middle;
this.LastName = last;
}
public class Builder
{
private Person person = new Person();
public Builder WithFirstName(string first)
{
person.FirstName = first;
return this;
}
public Builder WithMiddleName(string middle)
{
person.MiddleName = middle;
return this;
}
public Builder WithLastName(string last)
{
person.LastName = last;
return this;
}
public IPerson Build()
{
if (person.FirstName != null
&& person.MiddleName != null
&& person.LastName != null)
return person;
throw new Exception("Cannot build person because reasons...");
}
}
}
Then use it as follows:
var person = new Person.Builder()
.WithFirstName("Rob")
.WithMiddleName("<I have no middle name>")
.WithLastName("Janssen")
.Build();
You can use the With... methods in any order you like:
var person = new Person.Builder()
.WithLastName("Janssen")
.WithFirstName("Rob")
.WithMiddleName("<I have no middle name>")
.Build();
Fiddle here
Edit: Crap; I din't notice the other requirement of compile time errors.
This does, however, 'force' you to set all 3 required fields and 'forces' you to "name" the field using the With... methods which makes it hard to confuse the values and also allows you to specify the values in the order you desire. It also prevents you from instantiating a Person yourself and also allows you to have your private setters (e.g. 'read only' properties) and keeps your interface intact. The only thing missing here is compile time errors; you won't get one here. Maybe Code Contracts can help in that department, but that would at least require some 'team coordination' (e.g. everyone needs to install an extension and have static checking enabled); and I'm not 100% sure if it can be done with code contracts either.
No, interfaces aren't designed to behave that way.
An interface assure you that the classes who implements it will have all the elements declared in the interface. But doesn't make any statements on how they are going to be implemented.
You can accomplish what you want with an abstract class with a custom constructor.
public abstract class Person
{
public abstract string FirstName {get; protected set; }
public abstract string MiddleName { get; protected set; }
public abstract string LastName { get; protected set; }
public Person(string firstName, string middleName, string lastName){
FirstName = firstName;
MiddleName = middleName;
LastName = lastName;
}
}
public class PersonNeedAllProperties : Person
{
public override string FirstName{get; protected set;}
public override string MiddleName{get; protected set;}
public override string LastName{get; protected set;}
public PersonNeedAllProperties(
string firstName,
string lastName,
string middleName)
: base(firstName, lastName, middleName)
{
}
}
The custom constructor force you to complete the names in the child classes.
You can combine this answer with the one of John Gardner using classes instead of strings for the names (firstName, middleName, lastName) to simulate the named parameters.
You need to be aware of the limitations of this method, in C# you cannot inherit from more than one class, so you need to keep this restriction in mind if you decide to implement this solution.
Related
I was folowing the tutorial at youtube. At 8:04 instructor attached the local method to overloaded ToString, when I try to do same I don't see the overloaded method in intellisense.
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime StartDate { get; set; }
public int Rating { get; set; }
public override string ToString()
{
return string.Format("{0} {1}", FirstName, LastName);
}
public string ToString(PersonFormat format)
{
return format(this);
}
I tried adding the keyword override too it does not work.Looks like I am missing something somewhere.
This is because var is hiding the actual type of the person which is object when accessed through PersonListBox.Items. The below works:
foreach(Person person in PersonListBox.Items)
{
}
While :
foreach(var person in PersonListBox.Items)
{
// person is object. It doesn't have the method you want to call.
// it even doesn't have FirstName or LastName
// try person. and you can clearly see it only has the common
// object methods.
}
Hint: you can also use foreach(var p in PersonListBox.Items.OfType<Person>())
I'm trying to built some generic code for tables with the same columns but i have a problem figuring out how to use a generic "resolver" in order to use only one variable.
I tried to use dynamic without success and i'm out of ideas.
Entity1, Entity2 and Entity3 have exactly the same fields (automatically generated from Entity Framework) but there are different tables. Is possible to use only one variable to get the results?
var repository1 = new RepositoryBase<Entity1>();
var repository2 = new RepositoryBase<Entity2>();
var repository3 = new RepositoryBase<Entity3>();
Enum RepositoryTypes
{
Repo1 = 0,
Repo2 = 1,
Repo3 = 2
}
//Method Definition: RepositoryResolver(RepositoryTypes repoTypes);
var repository = RepositoryResolver(RepositoryTypes.Repo1); //Example. The RepositoryResolver should return one of the 3 RepositoryBase<EntityX>
var results = repository.Get(record => record.Name.Contains("fist name")).ToList();
foreach(var person in results)
{
Console.WriteLine("First Name: {0}, Last Name: {1}", person.Name, person.LastName);
}
Thanks!
Edit
Here is an example of the Entities:
Entity1 - Belongs to DataBase1
public string Name;
public string LastName;
public string Age;
Entity2 - Belongs to DataBase2
public string Name;
public string LastName;
public string Age;
Entity3 - Belongs to DataBase3
public string Name;
public string LastName;
public string Age;
If you have access to the Entity1 (and 2 and 3) declarations, either base them on one abstract class or create and have them implement an interface with said fields. You can then declare the variable through the abstract base class or the interface and voila, you're in the game. Something along the lines of (writing off the top of my head without compiling or intellisense, so there might be typos'n'stuff):
public interface IEntity
{
string Name { get; set;}
string LastName { get; set;}
}
public class Entity1 : IEntity
{
public string Name { get; set;}
public string LastName { get; set;}
}
public class Entity2 : IEntity
{
public string Name { get; set;}
public string LastName { get; set;}
}
//...and later in your main code...
foreach(IEntity person in results)
{
Console.WriteLine("First Name: {0}, Last Name: {1}", person.Name, person.LastName);
}
My person class:
class Person
{
public string FirstName { get; private set; }
public string LastName { get; private set; }
public int Age { get; private set; }
public Person(string firstName,string LastName,int age)
{
this.FirstName = firstName;
this.LastName = LastName;
this.Age = age;
}
public override string ToString()
{
return this.FirstName + " " + this.LastName + " " + this.Age;
}
}
Main:
class Program
{
static void Main(string[] args)
{
Person sallyPerson = new Person("Sally", "Solomon",23);
}
}
Lets say I want to change the Firstname and Age of the person, how would I go about doing so? The FirstName and Age properties are set privately.
You could make the properties public -
class Person
{
// changed private set; to set;
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
So I just realized, all of the answers are assuming that the question you're asking is literally the question you're asking, rather than just an example. But perhaps you were just using it as an example, and you actually need to set a private property of an object that you don't control the source of? In which case, I would say, that's generally a bad idea, but if you really need to do it anyway, there is a way. That way is called "reflection" (this being just one of many kind-of-sketchy things you can do with reflection if you really want). This is a question that you might want to look at, if that is actually what you're after.
More likely, though, the correct response is just don't have the properties be private if you control the source for that class, and are going to want to change them.
class Program
{
static void Main(string[] args)
{
Person sallyPerson = new Person("Sally", "Solomon",23);
sallyPerson = new Person("Newname", "Newvalue", 23);
}
}
This would be one way, another way would be to add methods in the Person object:
public void UpdateFirstName(string newName)
{
this.FirstName = newName;
}
and do sallyPerson.UpdateFirstName("newName");
Or make the properties public with public set and get
The class has been set up as Immutable meaning that once an instance of this class has been constructed it's properties cannot be modified. If you want to be able to construct this class and them modify it's properties you need to make the classes set properties to be public, like so:
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
I'm hoping this is a homework question, otherwise #Brandon Cuff's answer is the obvious one.
Alternatively, you could expose methods to set the fields:
class Person
{
public void SetFirstName(string value)
{
this.FirstName = value;
}
}
I'm trying to figure out a good way to approach this. I have a Customer class which implements the ICustomer interface. This interface has a number of properties in it:
public interface ICustomer
{
string FirstName {get; set;}
string LastName {get; set;}
}
I only want certain classes to have the ability to set those properties however; namely, those classes in the project. So I thought about making the setter internal:
public class Customer : ICustomer
{
string FirstName {get; internal set;}
string LastName {get; internal set;}
}
I'd like to mark that setter as internal in the interface however, so there's no chance someone implements ICustomer and someone outside the assembly modifies those properties. Is there a good way to do this?
The properties in the interface should be read only. It's acceptable for the concrete class that implements the interface to have a setter even if none is defined in the interface.
public interface ICustomer
{
string FirstName { get; }
string LastName { get; }
}
public class Customer : ICustomer
{
public string FirstName { get; internal set; }
public string LastName { get; internal set; }
}
If it's really important that the setter be exposed through an interface, rather than having the interface being entirely read-only, you can use something like this:
public interface IReadCustomer
{
string FirstName { get; }
string LastName { get; }
}
internal interface IWriteCustomer
{
string FirstName { set; }
string LastName { set; }
}
internal interface IReadWriteCustomer : IReadCustomer, IWriteCustomer
{ }
public class Customer : IReadWriteCustomer
{
private string _firstName;
private string _lastName;
public string FirstName
{
get { return _firstName; }
internal set { _firstName = value; }
}
public string LastName
{
get { return _lastName; }
internal set { _lastName = value; }
}
string IReadCustomer.FirstName
{
get { return FirstName; }
}
string IReadCustomer.LastName
{
get { return LastName; }
}
string IWriteCustomer.FirstName
{
set { FirstName = value; }
}
string IWriteCustomer.LastName
{
set { LastName = value; }
}
}
I'd like to mark that setter as internal in the interface however, so there's no chance someone implements ICustomer and someone outside the assembly modifies those properties. Is there a good way to do this?
No. Property members are always public, unfortunately. Additionally, messing around with access levels on properties where part of it is specified on the interface gets painful, IIRC. What you can do is this:
public interface ICustomer
{
string FirstName { get; }
string SecondName { get; }
}
internal interface ICustomerWithSetMethods : ICustomer
{
void SetFirstName(string name);
void SetLastName(string name);
}
public class Customer : ICustomerWithSetMethods
Then from the outside it'll look like Customer only implements ICustomer, but from inside your code will see that it implements ICustomerWithSetMethods.
Unfortunately that doesn't play nicely if your API needs to declare any public methods where you'd really like to just declare a return type of ICustomer, but you'll actually know that it's always ICustomerWithSetMethods.
Assuming you still want to allow multiple implementations, you could potentially go for an abstract class instead:
public abstract class CustomerBase
{
public abstract string FirstName { get; }
public abstract string LastName { get; }
internal abstract void SetFirstName(string name);
internal abstract void SetLastName(string name);
}
Now we have the slight oddity that no-one outside the assembly can extend your CustomerBase, because there are abstract methods they'd have to override that they can't even see - but it does mean you can use CustomerBase everywhere in your API.
This is the approach we took in Noda Time for calendar systems in the end - I blogged about it when I first came up with the plan. I generally prefer interfaces to abstract classes, but the benefit here was significant.
I have a class:
public class Person
{
public string FirstName { get; private set; }
public string LastName { get; private set; }
public string Email { get; private set; }
public string Telephone { get; private set; }
public Address Address { get; private set; }
public Person(string firstName, string lastName)
{
//do null-checks
FirstName = firstName;
LastName = lastName;
Address = new Address();
}
public void AddOrChangeEmail(string email)
{
//Check if e-mail is a valid e-mail here
Email = email;
}
public void AddOrChangeTelephone(string telephone)
{
//Check if thelephone has correct format and valid symbols
Telephone = telephone;
}
public void AddOrChangeAdress(Address address)
{
Address = address;
}
The properties that are not in the constructor are optional, i.e. the person does not need an e-mail, address or telephone. However, I want to give the user of the class an opportunity to create the object without having to first provide the required information and then afterwards have to find out what methods to use to add the information.
Questions:
Is it right to create 3 additional overloads to give them that option?
Should i allow public setters on the optional properties and do the validation there?
If the person marries and changes last name, do I need additional method to change the last name or should I make this setter public too, and just require them in constructor?
No.
Yes
Make it public.
Assuming you are going to have more code in the AddOrChange methods such as formatting logic or validation then I'd do the following. Otherwise, I'd completely get rid of the AddOrChange methods:
public class Person
{
private string _email = string.empty;
private string _telephone = string.empty;
private Address _address = new Address();
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email {
get { return _email }
set { AddOrChangeEmail(value); }
}
public string Telephone {
get { return _telephone;}
set { AddOrChangeTelephone(value); }
}
public Address Address {
get { return _address; }
set { AddOrChangeAddress(value); }
}
public Person(string firstName, string lastName)
{
//do null-checks
FirstName = firstName;
LastName = lastName;
}
private void AddOrChangeEmail(string email)
{
//Check if e-mail is a valid e-mail here
_email = email;
}
private void AddOrChangeTelephone(string telephone)
{
//Check if thelephone has correct format and valid symbols
_telephone = telephone;
}
private void AddOrChangeAddress(Address address)
{
_address = address;
}
}
To work with this class you could do any of the following:
Person p = new Person("Tom", "Jones");
p.Telephone = "9995551111";
or
Person p = new Person("Tom", "Jones") { Telephone = "9995551111", Email="spamme#ms.com" }
AddOrChange is equivalent to simple property with public setter, thus you don't need those methods.
public class Person
{
public string FirstName { get; private set; }
public string LastName { get; private set; }
public Email Email { get; set; }
public Telephone Telephone { get; set; }
public Address Address { get; set; }
public Person(string firstName, string lastName)
{
//do null-checks
FirstName = firstName;
LastName = lastName;
}
}
If user during person creation want to provide something additional beside required data, she can use class initializers. Also you can add some optional parameters to constructor.
var bob = new Person("Bob", "Uncle") { Address = someAddress };
If its OK for person to relocate, then why not to use public setter for changing address? Of course, you should check if address is valid. Also if relocating is a business process (i.e. you are relocating someone in hotel) then it would be nice to have this operation on domain service (which will verify if destination room is empty and ready).
Allowing to change name is OK. Usually name is not identity for such entities, thus it could change.
Also, I'd introduced value objects for email and telephone. I think it is not person's responsibility to verify if email address valid. Move that to Email class. Same with Phone and Address.
Is lots of add/change methods and constructor overloads a consequence of DDD?
No, lots of updating methods is not consequence of DDD.
Code
Your Person class can be rewritten to have only 2 updating methods:
class Person
public function Rename(FirstName as Name, LastName as Name) as Person
public function ChangeContacts(
Address as Maybe(of Address),
Phone as Maybe(of Phone),
Mail as Maybe(of MailAddress)) as Person
end class
Rename method accepts two required parameters of special Name type. Validation checks for names happen when names are created, not when they are passed into Person class.
ChangeContacts method accepts three optional parameters any of which can be absent. Special Maybe type indicates that they are optional. Special Address, Phone and MailAddress types indicate that these parameters are already valid and there is no need to validate them again in Person class.
Use case
Person marries and changes last name
Person = Person.
Rename(Person.FirstName, LastNameAfterMarriage)
Person buys new phone number
Person = Person.
ChangeContacts(Person.Address, NewPhoneNumber, Person.Mail)
Person lost phone number
Dim NewPhoneNumber = Maybe.Create(Nothing)
Person = Person.
ChangeContacts(Person.Address, NewPhoneNumber, Person.Mail)
The pattern is to call updating method with old values + some new values.