Filter Pattern with generic TypeOf
up vote
1
down vote
favorite
I'm implementing the filter (criteria) pattern and I'm not sure what the best approach is to create a generic TypeOfCriteria.
To give some context, part of the filtering system is specific, for example I have an EmailAddressContains filter inside a UserCriteria folder. The user class is abstract and there are several different usertypes inheriting from that base class, for example AdminUser and FullControlUser. Other parts of the system have similar inheritance trees. At first I implemented specific TypeOf criteria like TypeOfAdminUser, but I soon figured I could make this more generic.
I came up with three approaches and I'm not sure which is "the best". Or perhaps there's another approach which I haven't thought of yet.
Approach 1
public interface ICriteria<T> where T : BaseEntity
{
IEnumerable<T> MeetCriteria(IEnumerable<T> entities);
}
public class TypeOfCriteria<T> : ICriteria<T> where T : BaseEntity
{
private readonly Type _type;
public TypeOfCriteria(Type type)
{
_type = type;
}
public IEnumerable<T> MeetCriteria(IEnumerable<T> entities)
{
return from T entity in entities
where entity.GetType().Equals(_type)
select entity;
}
}
Usage:
ICriteria<User> criteria = new TypeOfCriteria<User>(typeof(AdminUser));
admins = criteria.MeetCriteria(users);
Approach 2
public interface ICriteria<T> where T : BaseEntity
{
IEnumerable<T> MeetCriteria(IEnumerable<T> entities);
}
public class TypeOfCriteria<T, TResult> : ICriteria<T>
where T : BaseEntity
where TResult : BaseEntity
{
public IEnumerable<T> MeetCriteria(IEnumerable<T> entities)
{
return entities.OfType<TResult>().Cast<T>();
}
}
Usage:
ICriteria<User> criteria = new TypeOfCriteria<User, AdminUser>();
admins = criteria.MeetCriteria(users);
Approach 3
public interface ICriteria<T> where T : BaseEntity
{
IEnumerable<T> MeetCriteria<TResult>(IEnumerable<T> entities);
}
public class TypeOfCriteria<T> : ICriteria<T> where T : BaseEntity
{
public IEnumerable<T> MeetCriteria<TResult>(IEnumerable<T> entities)
{
return entities.OfType<TResult>().Cast<T>();
}
}
Usage:
ICriteria<User> criteria = new TypeOfCriteria<User>();
admins = criteria.MeetCriteria<AdminUser>(users);
Pros & Cons
Approach 1 feels the most like my other Criteria classes, where I pass the actual criteria in the constructor (new EmailAddressContains(".com")).
The MeetCriteria implementation of Approach 2 feels a bit cleaner. I'm not sure about performance, this looks like it could be faster, but that's not really a metric that I worry about for this project.
I like Approach 3 the least, because it requires me to change the interface.
c# generics software-design
|
show 10 more comments
up vote
1
down vote
favorite
I'm implementing the filter (criteria) pattern and I'm not sure what the best approach is to create a generic TypeOfCriteria.
To give some context, part of the filtering system is specific, for example I have an EmailAddressContains filter inside a UserCriteria folder. The user class is abstract and there are several different usertypes inheriting from that base class, for example AdminUser and FullControlUser. Other parts of the system have similar inheritance trees. At first I implemented specific TypeOf criteria like TypeOfAdminUser, but I soon figured I could make this more generic.
I came up with three approaches and I'm not sure which is "the best". Or perhaps there's another approach which I haven't thought of yet.
Approach 1
public interface ICriteria<T> where T : BaseEntity
{
IEnumerable<T> MeetCriteria(IEnumerable<T> entities);
}
public class TypeOfCriteria<T> : ICriteria<T> where T : BaseEntity
{
private readonly Type _type;
public TypeOfCriteria(Type type)
{
_type = type;
}
public IEnumerable<T> MeetCriteria(IEnumerable<T> entities)
{
return from T entity in entities
where entity.GetType().Equals(_type)
select entity;
}
}
Usage:
ICriteria<User> criteria = new TypeOfCriteria<User>(typeof(AdminUser));
admins = criteria.MeetCriteria(users);
Approach 2
public interface ICriteria<T> where T : BaseEntity
{
IEnumerable<T> MeetCriteria(IEnumerable<T> entities);
}
public class TypeOfCriteria<T, TResult> : ICriteria<T>
where T : BaseEntity
where TResult : BaseEntity
{
public IEnumerable<T> MeetCriteria(IEnumerable<T> entities)
{
return entities.OfType<TResult>().Cast<T>();
}
}
Usage:
ICriteria<User> criteria = new TypeOfCriteria<User, AdminUser>();
admins = criteria.MeetCriteria(users);
Approach 3
public interface ICriteria<T> where T : BaseEntity
{
IEnumerable<T> MeetCriteria<TResult>(IEnumerable<T> entities);
}
public class TypeOfCriteria<T> : ICriteria<T> where T : BaseEntity
{
public IEnumerable<T> MeetCriteria<TResult>(IEnumerable<T> entities)
{
return entities.OfType<TResult>().Cast<T>();
}
}
Usage:
ICriteria<User> criteria = new TypeOfCriteria<User>();
admins = criteria.MeetCriteria<AdminUser>(users);
Pros & Cons
Approach 1 feels the most like my other Criteria classes, where I pass the actual criteria in the constructor (new EmailAddressContains(".com")).
The MeetCriteria implementation of Approach 2 feels a bit cleaner. I'm not sure about performance, this looks like it could be faster, but that's not really a metric that I worry about for this project.
I like Approach 3 the least, because it requires me to change the interface.
c# generics software-design
1
Its worth noting that method 1 can give different results to the other two. OfType will not just match on exact type but also match anything derived from its type. So for example if I do.OfType<Base>()
that will return all types that areBase
or are derived fromBase
. This may not be relevant in your specific case but it is worth keeping in mind.
– Chris
Nov 7 at 9:17
What is the purpose of putting all this in a separate class instead of usingOfType
directly, or writing an extension method that enhances its functionality? Seems like a pain to instantiate a class just to filter a list.
– John Wu
Nov 7 at 9:19
2
One other thing is that in approach2 you don't actually need the Cast due to covariance. ieIEnumerable<Derived>
can be assigned toIEnumerable<Base>
– Chris
Nov 7 at 9:20
2
Approach 2 is fine, as it use generics all the way , is verifiable at compile time, there's not much difference between 2 & 3, but its preferred to have generic class or generic method, you are mixing both in Approach 3 and it doesn't have the constraint for theTResult
– Mrinal Kamboj
Nov 7 at 9:31
1
Looking at constraints should the constraint in 2 beTResult : T
?
– Chris
Nov 7 at 9:33
|
show 10 more comments
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I'm implementing the filter (criteria) pattern and I'm not sure what the best approach is to create a generic TypeOfCriteria.
To give some context, part of the filtering system is specific, for example I have an EmailAddressContains filter inside a UserCriteria folder. The user class is abstract and there are several different usertypes inheriting from that base class, for example AdminUser and FullControlUser. Other parts of the system have similar inheritance trees. At first I implemented specific TypeOf criteria like TypeOfAdminUser, but I soon figured I could make this more generic.
I came up with three approaches and I'm not sure which is "the best". Or perhaps there's another approach which I haven't thought of yet.
Approach 1
public interface ICriteria<T> where T : BaseEntity
{
IEnumerable<T> MeetCriteria(IEnumerable<T> entities);
}
public class TypeOfCriteria<T> : ICriteria<T> where T : BaseEntity
{
private readonly Type _type;
public TypeOfCriteria(Type type)
{
_type = type;
}
public IEnumerable<T> MeetCriteria(IEnumerable<T> entities)
{
return from T entity in entities
where entity.GetType().Equals(_type)
select entity;
}
}
Usage:
ICriteria<User> criteria = new TypeOfCriteria<User>(typeof(AdminUser));
admins = criteria.MeetCriteria(users);
Approach 2
public interface ICriteria<T> where T : BaseEntity
{
IEnumerable<T> MeetCriteria(IEnumerable<T> entities);
}
public class TypeOfCriteria<T, TResult> : ICriteria<T>
where T : BaseEntity
where TResult : BaseEntity
{
public IEnumerable<T> MeetCriteria(IEnumerable<T> entities)
{
return entities.OfType<TResult>().Cast<T>();
}
}
Usage:
ICriteria<User> criteria = new TypeOfCriteria<User, AdminUser>();
admins = criteria.MeetCriteria(users);
Approach 3
public interface ICriteria<T> where T : BaseEntity
{
IEnumerable<T> MeetCriteria<TResult>(IEnumerable<T> entities);
}
public class TypeOfCriteria<T> : ICriteria<T> where T : BaseEntity
{
public IEnumerable<T> MeetCriteria<TResult>(IEnumerable<T> entities)
{
return entities.OfType<TResult>().Cast<T>();
}
}
Usage:
ICriteria<User> criteria = new TypeOfCriteria<User>();
admins = criteria.MeetCriteria<AdminUser>(users);
Pros & Cons
Approach 1 feels the most like my other Criteria classes, where I pass the actual criteria in the constructor (new EmailAddressContains(".com")).
The MeetCriteria implementation of Approach 2 feels a bit cleaner. I'm not sure about performance, this looks like it could be faster, but that's not really a metric that I worry about for this project.
I like Approach 3 the least, because it requires me to change the interface.
c# generics software-design
I'm implementing the filter (criteria) pattern and I'm not sure what the best approach is to create a generic TypeOfCriteria.
To give some context, part of the filtering system is specific, for example I have an EmailAddressContains filter inside a UserCriteria folder. The user class is abstract and there are several different usertypes inheriting from that base class, for example AdminUser and FullControlUser. Other parts of the system have similar inheritance trees. At first I implemented specific TypeOf criteria like TypeOfAdminUser, but I soon figured I could make this more generic.
I came up with three approaches and I'm not sure which is "the best". Or perhaps there's another approach which I haven't thought of yet.
Approach 1
public interface ICriteria<T> where T : BaseEntity
{
IEnumerable<T> MeetCriteria(IEnumerable<T> entities);
}
public class TypeOfCriteria<T> : ICriteria<T> where T : BaseEntity
{
private readonly Type _type;
public TypeOfCriteria(Type type)
{
_type = type;
}
public IEnumerable<T> MeetCriteria(IEnumerable<T> entities)
{
return from T entity in entities
where entity.GetType().Equals(_type)
select entity;
}
}
Usage:
ICriteria<User> criteria = new TypeOfCriteria<User>(typeof(AdminUser));
admins = criteria.MeetCriteria(users);
Approach 2
public interface ICriteria<T> where T : BaseEntity
{
IEnumerable<T> MeetCriteria(IEnumerable<T> entities);
}
public class TypeOfCriteria<T, TResult> : ICriteria<T>
where T : BaseEntity
where TResult : BaseEntity
{
public IEnumerable<T> MeetCriteria(IEnumerable<T> entities)
{
return entities.OfType<TResult>().Cast<T>();
}
}
Usage:
ICriteria<User> criteria = new TypeOfCriteria<User, AdminUser>();
admins = criteria.MeetCriteria(users);
Approach 3
public interface ICriteria<T> where T : BaseEntity
{
IEnumerable<T> MeetCriteria<TResult>(IEnumerable<T> entities);
}
public class TypeOfCriteria<T> : ICriteria<T> where T : BaseEntity
{
public IEnumerable<T> MeetCriteria<TResult>(IEnumerable<T> entities)
{
return entities.OfType<TResult>().Cast<T>();
}
}
Usage:
ICriteria<User> criteria = new TypeOfCriteria<User>();
admins = criteria.MeetCriteria<AdminUser>(users);
Pros & Cons
Approach 1 feels the most like my other Criteria classes, where I pass the actual criteria in the constructor (new EmailAddressContains(".com")).
The MeetCriteria implementation of Approach 2 feels a bit cleaner. I'm not sure about performance, this looks like it could be faster, but that's not really a metric that I worry about for this project.
I like Approach 3 the least, because it requires me to change the interface.
c# generics software-design
c# generics software-design
asked Nov 7 at 9:06
Rik D
393
393
1
Its worth noting that method 1 can give different results to the other two. OfType will not just match on exact type but also match anything derived from its type. So for example if I do.OfType<Base>()
that will return all types that areBase
or are derived fromBase
. This may not be relevant in your specific case but it is worth keeping in mind.
– Chris
Nov 7 at 9:17
What is the purpose of putting all this in a separate class instead of usingOfType
directly, or writing an extension method that enhances its functionality? Seems like a pain to instantiate a class just to filter a list.
– John Wu
Nov 7 at 9:19
2
One other thing is that in approach2 you don't actually need the Cast due to covariance. ieIEnumerable<Derived>
can be assigned toIEnumerable<Base>
– Chris
Nov 7 at 9:20
2
Approach 2 is fine, as it use generics all the way , is verifiable at compile time, there's not much difference between 2 & 3, but its preferred to have generic class or generic method, you are mixing both in Approach 3 and it doesn't have the constraint for theTResult
– Mrinal Kamboj
Nov 7 at 9:31
1
Looking at constraints should the constraint in 2 beTResult : T
?
– Chris
Nov 7 at 9:33
|
show 10 more comments
1
Its worth noting that method 1 can give different results to the other two. OfType will not just match on exact type but also match anything derived from its type. So for example if I do.OfType<Base>()
that will return all types that areBase
or are derived fromBase
. This may not be relevant in your specific case but it is worth keeping in mind.
– Chris
Nov 7 at 9:17
What is the purpose of putting all this in a separate class instead of usingOfType
directly, or writing an extension method that enhances its functionality? Seems like a pain to instantiate a class just to filter a list.
– John Wu
Nov 7 at 9:19
2
One other thing is that in approach2 you don't actually need the Cast due to covariance. ieIEnumerable<Derived>
can be assigned toIEnumerable<Base>
– Chris
Nov 7 at 9:20
2
Approach 2 is fine, as it use generics all the way , is verifiable at compile time, there's not much difference between 2 & 3, but its preferred to have generic class or generic method, you are mixing both in Approach 3 and it doesn't have the constraint for theTResult
– Mrinal Kamboj
Nov 7 at 9:31
1
Looking at constraints should the constraint in 2 beTResult : T
?
– Chris
Nov 7 at 9:33
1
1
Its worth noting that method 1 can give different results to the other two. OfType will not just match on exact type but also match anything derived from its type. So for example if I do
.OfType<Base>()
that will return all types that are Base
or are derived from Base
. This may not be relevant in your specific case but it is worth keeping in mind.– Chris
Nov 7 at 9:17
Its worth noting that method 1 can give different results to the other two. OfType will not just match on exact type but also match anything derived from its type. So for example if I do
.OfType<Base>()
that will return all types that are Base
or are derived from Base
. This may not be relevant in your specific case but it is worth keeping in mind.– Chris
Nov 7 at 9:17
What is the purpose of putting all this in a separate class instead of using
OfType
directly, or writing an extension method that enhances its functionality? Seems like a pain to instantiate a class just to filter a list.– John Wu
Nov 7 at 9:19
What is the purpose of putting all this in a separate class instead of using
OfType
directly, or writing an extension method that enhances its functionality? Seems like a pain to instantiate a class just to filter a list.– John Wu
Nov 7 at 9:19
2
2
One other thing is that in approach2 you don't actually need the Cast due to covariance. ie
IEnumerable<Derived>
can be assigned to IEnumerable<Base>
– Chris
Nov 7 at 9:20
One other thing is that in approach2 you don't actually need the Cast due to covariance. ie
IEnumerable<Derived>
can be assigned to IEnumerable<Base>
– Chris
Nov 7 at 9:20
2
2
Approach 2 is fine, as it use generics all the way , is verifiable at compile time, there's not much difference between 2 & 3, but its preferred to have generic class or generic method, you are mixing both in Approach 3 and it doesn't have the constraint for the
TResult
– Mrinal Kamboj
Nov 7 at 9:31
Approach 2 is fine, as it use generics all the way , is verifiable at compile time, there's not much difference between 2 & 3, but its preferred to have generic class or generic method, you are mixing both in Approach 3 and it doesn't have the constraint for the
TResult
– Mrinal Kamboj
Nov 7 at 9:31
1
1
Looking at constraints should the constraint in 2 be
TResult : T
?– Chris
Nov 7 at 9:33
Looking at constraints should the constraint in 2 be
TResult : T
?– Chris
Nov 7 at 9:33
|
show 10 more comments
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
active
oldest
votes
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53186327%2ffilter-pattern-with-generic-typeof%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
1
Its worth noting that method 1 can give different results to the other two. OfType will not just match on exact type but also match anything derived from its type. So for example if I do
.OfType<Base>()
that will return all types that areBase
or are derived fromBase
. This may not be relevant in your specific case but it is worth keeping in mind.– Chris
Nov 7 at 9:17
What is the purpose of putting all this in a separate class instead of using
OfType
directly, or writing an extension method that enhances its functionality? Seems like a pain to instantiate a class just to filter a list.– John Wu
Nov 7 at 9:19
2
One other thing is that in approach2 you don't actually need the Cast due to covariance. ie
IEnumerable<Derived>
can be assigned toIEnumerable<Base>
– Chris
Nov 7 at 9:20
2
Approach 2 is fine, as it use generics all the way , is verifiable at compile time, there's not much difference between 2 & 3, but its preferred to have generic class or generic method, you are mixing both in Approach 3 and it doesn't have the constraint for the
TResult
– Mrinal Kamboj
Nov 7 at 9:31
1
Looking at constraints should the constraint in 2 be
TResult : T
?– Chris
Nov 7 at 9:33