Probably the answer is you can't. However, I would like to have a work-around to solve my problem.
Objective
I am trying to create a program in which I try to avoid nulls as much as possible.
Problem
I am using the empty object pattern in places where null could have been returned. Like this:
public User GetByEmail(string email){
//search user on database given unique email
if(!WasFound())
return User.Empty;
}
and here is the User Domain model.
//User has more fields but for simplicity I omitted them
public class User {
public User(string username,string email){
this.Username = username;
this.Email = email;
}
//...
//probaly not the best default values but I trivially chose them for explanation.
private static readonly User _empty = new User("*","[email protected]");
public static User Empty{
get{return _empty;}
}
}
So far so good. However, I am planning that the static Empty function be implemented by the majority of my domain models. Also, Someone else might create a domain and I want to force them to implement the Empty function. So I created a DataModel class that will be the base of all domain Models.
public abstract class DataModel<TModel>: BaseEntity, IEmptiable<TModel> where TModel : new()
{
private bool _isEmpty = false;
public virtual static TModel Empty {
get {
_isEmpty = true;
return CreateEmpty();
}
}
public virtual bool IsEmpty() => _isEmpty;
protected abstract TModel CreateEmpty();
}
And my new User class would look like this.
public class User: DataModel<User> {
public User(string username,string email){
this.Username = username;
this.Email = email;
}
//...
private readonly User _empty = new User("*",[email protected]);
protected User CreateEmpty(){
return _empty;
}
}
In that way I am forcing the developer to implement a CreateEmpty() class and the Empty function will be present in the domain if you extend the DataModel abstract class. My problem is that Empty function is static and is giving me many problems. Is there a work-around to this problem? My vision is to be able to execute something like:
Account.Empty
//and to check if is empty with
if(Account.IsEmpty())
//Plan B
Keep in mind that this is pseudocode. (I know that static function can't user non-static variables declared outside of the function.)
Any help will be much appreciated.
3 Answers 3
You indeed can't inherit a static function. But if you need that static function to create an empty object, then you can also require that the derived classes implement a particular constructor.
The base class could then be, for example
public abstract class DataModel<TModel>: BaseEntity, IEmptiable<TModel> where TModel : new()
{
protected struct EmptyTag {};
private bool _isEmpty = false;
private static TModel _empty = null;
public virtual static TModel Empty {
get {
if (_empty == null) {
_empty = new TModel(EmptyTag());
_empty._isEmpty = true;
}
return _empty;
}
}
public virtual bool IsEmpty() => _isEmpty;
}
public class User: DataModel<User> {
public User(DataModel<User>.EmptyTag){
this.Username = "*";
this.Email = "[email protected]";
}
public User(string username,string email){
this.Username = username;
this.Email = email;
}
//...
}
User aUser = User.Empty;
if (aUser.isEmpty())
// ...
You should look into the default operator. Returning
default(T)
would isolate other classes or generics from knowing specifics about what that would be.
Or pass the Empty T into DataModel so it doesn't have to have this knowledge and just compare against it.
You might use Activator (if the performance hit is acceptable) or ORM approach, e.g. - use Activator, yet cache the result; - expect a particular constructor, throw if not present, cache after first found; - expect a static private field of particular type that holds (inter alia) constructor method, throw if not present, cache after first found etc.
All the ORM methods i found uses reflection to some extent, yet it's not a problem with caching of an uninitialized instance.
At the moment I'm in deep religious confusion factory vs repository. So not sure whether you are heading the right direction.
Explore related questions
See similar questions with these tags.
if
statement in your code. Are you sure that much added complexity is called for, just because.IsEmpty()
looks better than== null
? Because if you still have thatif
statement, that's all it is.