Is there a better way to write this? As one class perhaps, instead of two.
using System;
namespace SnippetTool.Repositories
{
public abstract class ARepository<TProvider> where TProvider : class
{
protected TProvider Provider { get; set; }
protected ARepository(TProvider provider)
{
if (provider == null)
throw new ArgumentNullException("provider");
Provider = provider;
}
}
public abstract class ARepository<TProvider, TValidator> : ARepository<TProvider> where TProvider : class where TValidator : class
{
protected TValidator Validator { get; set; }
protected ARepository(TProvider provider, TValidator validator) : base(provider)
{
Validator = validator;
}
}
}
An open generic type is simply a generic type whose type parameters have not been specified. For example, IEnumerable<> is an open generic type, and IEnumerable (or string or whatever) is a **closed generic type**, as its type parameter has been specified.
To make a generic type optional, you have to assign the void as the default value. In the example below, even though the function takes a generic type T, still you can call this function without passing the generic type and it takes void as default.
Definition: “A generic type is a generic class or interface that is parameterized over types.” Essentially, generic types allow you to write a general, generic class (or method) that works with different types, allowing for code re-use.
Whenever you want to restrict the type parameter to subtypes of a particular class you can use the bounded type parameter. If you just specify a type (class) as bounded parameter, only sub types of that particular class are accepted by the current generic class.
I don't think you can do it as one class, currently, generally what I try to do in this situation is create the most general class (the one that takes the most generic args) to have all the logic, then make the more specific ones be subclasses that default those types.
For example, let's say we are writing a translator that translates from one type of value to another, so like a Dictionary
but also has defaults, etc.
We could define this as:
public class Translator<TKey, TValue, TDictionary> where TDictionary : IDictionary<TKey, TValue>, new();
{
private IDictionary<TKey, TValue> _map = new TDictionary();
...
}
This is my generic case, which can have any implementation of IDictionary
, but say we want a simpler version that always uses Dictionary
if not specified, we could do:
public class Translator<TKey, TValue> : Translator<TKey, TValue, Dictionary<TKey, TValue>>
{
// all this does is pass on the "default" for TDictionary...
}
In this way, I can make:
// uses Dictionary<int, string>
var generic = new Translator<int, string>();
// uses SortedDictionary instead
var specific = new Translator<int, string, SortedDictioanry<int, string>>();
So in your case, maybe your generic always has a TValidator property, but it's defaulted (maybe to always return true
in your most generic form?
For example, maybe you have a definition of a default validator (say called DefaultValidator
) you could reverse your definitions so that the more generic (the one that takes more generic type parameters) has all the logic and any specializations (fewer type parameters) are just subclasses that default those extra types:
using System;
namespace SnippetTool.Repositories
{
public class DefaultValidator
{
// whatever your "default" validation is, may just return true...
}
public abstract class ARepository<TProvider> : ARepository<TProvider, DefaultValidator>
where TProvider : class
{
protected ARepository(TProvider provider) : base(provider, new DefaultValidator());
{
}
// needs no new logic, just any specialized constructors...
}
public abstract class ARepository<TProvider, TValidator>
where TProvider : class
where TValidator : class
{
public TValidator Validator { get; set; }
protected ARepository(TProvider provider, TValidator validator)
{
Provider = provider;
Validator = validator;
}
// all the logic goes here...
}
}
UPDATE: Yes, based on your comment, if the TValidator
is an add-on (and not something defaulted), then layering it like you did is appropriate.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With