I'm trying to find a good design to a thing I'm working on. As an example, consider a TextProcessor class that accepts a string parameter and returns back a processed string.
Now in my implementation there are a number of different TextProcessors, each capable handling their own defined set of strings. There are two notable methods in each processor:
bool CanProcess(string text);
and
string Process(string text);
In reality these can be marked as static but for the sake of implementing a common interface I'm not setting them as static.
In addition to these TextProcessors there is a static TextProcessorFinder class. As the name implies, it finds the best TextProcessor and processes the input.
public static class TextProcessorFinder
{
private static List<ITextProcessor> _processors;
static TextProcessorFinder()
{
_processors = Assembly.GetExecutingAssembly().GetTypes()
.Where(t => t.GetInterfaces().Contains(typeof(ITextProcessor))
&& t.IsClass && !t.IsAbstract)
.Select(t => (ITextProcessor)Activator.CreateInstance(t))
.Where(t => t.IsActive)
.ToList();
}
public static ITextProcessor GetTextProcessor(string text)
{
return _processors.Where(p => p.CanProcess(text))
.OrderByDescending(p => p.Priority)
.FirstOrDefault();
}
}
What I hate about this approach is that I have to create an instance of every known TextProcessor just to call their CanProcess function.
I have tried creating an attribute with a simple Func to emulate the CanProcess function:
[AttributeUsage(AttributeTargets.Class)]
public class TextProcessorAttribute : Attribute
{
private Func<string, bool> func;
public TextProcessorAttribute(Func<string, bool> func)
{
this.func = func;
}
}
public interface ITextProcessor
{
bool IsActive { get; }
int Priority { get; }
bool CanProcess(string text);
string Process(string text);
}
// Hard-coded to true
[TextProcessor((s) => { return true; })]
public class SampleTextProcessor : ITextProcessor
{
// Implement ITextProcessor
}
Sadly however, Func is not a valid attribute parameter type.
What is the best way of doing this?
In my opinion, creating those instances is the best way to go. There really is no reason to not do it. Instances are cheap and creating them is cheap as well if you are not doing any work in the constructors - which you shouldn't do anyway.
With the CanProcess method you ask to each derived ITextProcessor if he can process the text. This is OK in my opinion.
Maybe there are some sort of specialization in the implementations of ITextProcessor, in this case you can create more specific interfaces, ITextTypeAProcessor, ITextTypeBProcessor, etc. Then you can filter by interface in your GetTextProcessor method.
Related
Background:
I am working with an organization that has an ever-growing collection of data types that they need to extract data from. I have no ability to change these data types. Some are machine-generated from XML files provided by other organizations; some are controlled by intransigent internal dev teams; and some are so old that no one is willing to change them in any way for fear that it will destabilize the entire Earth and cause it to crash into the sun. These classes don't share any common interface, and don't derive from any common type other than object. A few sample classes are given below for illustration purposes:
public class Untouchable
{
public string Data;
}
public class Unchangeable
{
public int Info;
}
The good news is that most of the time, I can use canned functionality to get at least some of the data from instances of the various classes. Unfortunately, most of these classes also have weird and specialized data that needs class-specific logic to extract data from. Also, information often needs to persist inside of the data extractors because the data objects I'm pulling data from have "interactions" (don't ask).
I have created an abstract generic Extractor<T> class to serve as a repository of common methodology, and an IExtractor<T> interface to serve as a convenient handle to access functionality. I also have a few specific (de-generic?) implementations of this class that can extract information from the business objects built from some of the data types. Here's some sample code to illustrate:
public interface IExtractor<T>
{
string ExtractionOne(T something);
string ExtractionTwo(T something);
}
public abstract class Extractor<T> : IExtractor<T>
{
private string internalInfo; // Certain business logic requires us to keep internal info over multiple objects that we extract data from.
protected Extractor() { internalInfo="stuff"; }
public virtual string ExtractionOne(T something)
{
return "This manipulation is generally applicable to most conceivable types.";
}
public abstract string ExtractionTwo(T something); // This DEFINITELY needs to be overridden for each class T
}
public class UntouchableExtractor : Extractor<Untouchable>
{
public UntouchableExtractor() : base() { }
public override string ExtractionTwo(Untouchable something)
{
return something.Data;
}
}
public class UnchangeableExtractor : Extractor<Unchangeable>
{
public UnchangeableExtractor() : base() { }
public override string ExtractionTwo(Unchangeable something)
{
return something.Info.ToString();
}
}
I don't yet support all of the available data types, but management wants to roll out the data extractor to end-users using a command-line interface. They're telling me that we should start extracting the data we can extract, and get to the rest later. Support for the many unmodifiable types will be added by me and by and other programmers as time permits, and the end-users are expected to work around our latency. This actually makes sense in our real-world setting, so just go with it.
The Problem:
The list of data types that we want to pull information from is very large. Maintaining an explicit list of supported types in code will be tricky and error prone -- especially if we find any problems with specific data extractors and need to revoke support until we fix some bugs.
I would like to support the large and changing list of supported data types from a single entry point that dynamically determines the "right version" of IExtractor<> to use based on a passed in dynamic dataObject. If there is no class that implements the IExtractor<> to support the given dataObject, than an error should be thrown.
What Doesn't Work:
I tried taking a dynamic thing and using typeof(Extractor<>).MakeGenericType(thing.GetType()) to create an instance of Extractor<Untouchable> or Extractor<Unchangeable>, but those are abstract classes, so I can't use Activator.CreateInstance() to build an instance of those classes. The core issue with this approach is that it's unfortunately looking for a class of the form Extractor<> instead of an interface of the form IExtractor<>.
I considered putting extension methods like IExtractor<T> BuildExtractor(this T something) in some class, but I'm nervous about running into some business logic called BuildExtractor that already exists in one of these untouchable classes. This may be an unhealthy level of paranoia, but that's where I'm at.
Where I need help:
I'd welcome any suggestions for how I can create a single entrypoint for an unconstrained collection of classes. Thanks in advance.
Combining some code from StackOverflow and my own testing, I suggest using Reflection to find all types implementing an interface:
public static class TypeExt {
public static bool IsBuiltin(this Type aType) => new[] { "/dotnet/shared/microsoft", "/windows/microsoft.net" }.Any(p => aType.Assembly.CodeBase.ToLowerInvariant().Contains(p));
public static IEnumerable<Type> ImplementingTypes(this Type interfaceType, bool includeAbstractClasses = false, bool includeStructs = false, bool includeSystemTypes = false, bool includeInterfaces = false) =>
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetLoadableTypes())
.Distinct()
.Where(aType => (includeAbstractClasses || !aType.IsAbstract) &&
(includeInterfaces ? aType != interfaceType : !aType.IsInterface) &&
(includeStructs || !aType.IsValueType) &&
(includeSystemTypes || !aType.IsBuiltin()) &&
interfaceType.IsAssignableFrom(aType) &&
aType.GetInterfaces().Contains(interfaceType));
}
public static class AssemblyExt {
//https://stackoverflow.com/a/29379834/2557128
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
if (assembly == null)
throw new ArgumentNullException("assembly");
try {
return assembly.GetTypes();
} catch (ReflectionTypeLoadException e) {
return e.Types.Where(t => t != null);
}
}
}
Reflection can be quite slow, and in my testing, getting all loaded types was the slowest part, so I added caching of the loaded types and the implementing types, but this does mean you will need to refresh the loaded types if you dynamically load assemblies:
public static class TypeExt {
public static bool IsBuiltin(this Type aType) => new[] { "/dotnet/shared/microsoft", "/windows/microsoft.net" }.Any(p => aType.Assembly.CodeBase.ToLowerInvariant().Contains(p));
static Dictionary<Type, HashSet<Type>> FoundTypes = null;
static List<Type> LoadableTypes = null;
public static void RefreshLoadableTypes() {
LoadableTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetLoadableTypes()).ToList();
FoundTypes = new Dictionary<Type, HashSet<Type>>();
}
public static IEnumerable<Type> ImplementingTypes(this Type interfaceType, bool includeAbstractClasses = false, bool includeStructs = false, bool includeSystemTypes = false, bool includeInterfaces = false) {
if (FoundTypes != null && FoundTypes.TryGetValue(interfaceType, out var ft))
return ft;
else {
if (LoadableTypes == null)
RefreshLoadableTypes();
var ans = LoadableTypes
.Where(aType => (includeAbstractClasses || !aType.IsAbstract) &&
(includeInterfaces ? aType != interfaceType : !aType.IsInterface) &&
(includeStructs || !aType.IsValueType) &&
(includeSystemTypes || !aType.IsBuiltin()) &&
interfaceType.IsAssignableFrom(aType) &&
aType.GetInterfaces().Contains(interfaceType))
.ToHashSet();
FoundTypes[interfaceType] = ans;
return ans;
}
}
}
public static class AssemblyExt {
//https://stackoverflow.com/a/29379834/2557128
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) {
if (assembly == null)
throw new ArgumentNullException("assembly");
try {
return assembly.GetTypes();
}
catch (ReflectionTypeLoadException e) {
return e.Types.Where(t => t != null);
}
}
}
Once you have one of these, you can make a factory method that takes a dynamic object:
public static class ImplementingFactory {
public static Type ExtractorType(dynamic anObject) {
Type oType = anObject.GetType();
var iType = typeof(IExtractor<>).MakeGenericType(oType);
var ans = iType.ImplementingTypes().FirstOrDefault();
if (ans == null)
throw new Exception($"Unable to find IExtractor<{oType.Name}>");
else
return ans;
}
}
The following snippet will create the concrete instance of Extractor<T> class and dynamically invokes a method of this instance
var test = new Unchangeable();
var baseType = typeof(Extractor<>).MakeGenericType(test.GetType());
var extractorType = Assembly.GetExecutingAssembly()
.GetTypes().FirstOrDefault(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(baseType));
if (extractorType != null)
{
dynamic extractor = Activator.CreateInstance(extractorType);
string result = extractor?.ExtractionTwo(test);
}
Of course, it's simplified, you can pass a specific instance of Unchangeable or Untouchable class and restrict assembly types scanning (and get all types only once).
The disadvantage here is that you have to pay attention to ExtractionOne and ExtractionTwo signatures, since they are invoked on dynamic object
The core issue with this approach is that it's unfortunately looking
for a class of the form Extractor<> instead of an interface of the
form IExtractor<>.
This snippet can help you to look through types using IExtrator<> interface
var baseType = typeof(IExtractor<>).MakeGenericType(typeof(Unchangeable));
var extractorType = Assembly.GetExecutingAssembly()
.GetTypes().FirstOrDefault(t => t.IsClass && !t.IsAbstract && baseType.IsAssignableFrom(t));
Reading this, I learned it was possible to allow a method to accept parameters of multiple types by making it a generic method. In the example, the following code is used with a type constraint to ensure "U" is an IEnumerable<T>.
public T DoSomething<U, T>(U arg) where U : IEnumerable<T>
{
return arg.First();
}
I found some more code which allowed adding multiple type constraints, such as:
public void test<T>(string a, T arg) where T: ParentClass, ChildClass
{
//do something
}
However, this code appears to enforce that arg must be both a type of ParentClass and ChildClass. What I want to do is say that arg could be a type of ParentClass or ChildClass in the following manner:
public void test<T>(string a, T arg) where T: string OR Exception
{
//do something
}
Your help is appreciated as always!
That is not possible. You can, however, define overloads for specific types:
public void test(string a, string arg);
public void test(string a, Exception arg);
If those are part of a generic class, they will be preferred over the generic version of the method.
Botz answer is 100% correct, here's a short explanation:
When you are writing a method (generic or not) and declaring the types of the parameters that the method takes you are defining a contract:
If you give me an object that knows how to do the set of things that
Type T knows how to do I can deliver either 'a': a return value of the
type I declare, or 'b': some sort of behavior that uses that type.
If you try and give it more than one type at a time (by having an or) or try to get it to return a value that might be more than one type that contract gets fuzzy:
If you give me an object that knows how to jump rope or knows how to calculate pi
to the 15th digit I'll return either an object that can go fishing or maybe mix
concrete.
The problem is that when you get into the method you have no idea if they've given you an IJumpRope or a PiFactory. Furthermore, when you go ahead and use the method (assuming that you've gotten it to magically compile) you're not really sure if you have a Fisher or an AbstractConcreteMixer. Basically it makes the whole thing way more confusing.
The solution to your problem is one of two possiblities:
Define more than one method that defines each possible transformation, behavior, or whatever. That's Botz's answer. In the programming world this is referred to as Overloading the method.
Define a base class or interface that knows how to do all the things that you need for the method and have one method take just that type. This may involve wrapping up a string and Exception in a small class to define how you plan on mapping them to the implementation, but then everything is super clear and easy to read. I could come, four years from now and read your code and easily understand what's going on.
Which you choose depends on how complicated choice 1 and 2 would be and how extensible it needs to be.
So for your specific situation I'm going to imagine you're just pulling out a message or something from the exception:
public interface IHasMessage
{
string GetMessage();
}
public void test(string a, IHasMessage arg)
{
//Use message
}
Now all you need are methods that transform a string and an Exception to an IHasMessage. Very easy.
If ChildClass means it is derived from ParentClass, you may just write the following to accept both ParentClass and ChildClass;
public void test<T>(string a, T arg) where T: ParentClass
{
//do something
}
On the otherhand, if you want to use two different types with no inheritance relation between them, you should consider the types implementing the same interface;
public interface ICommonInterface
{
string SomeCommonProperty { get; set; }
}
public class AA : ICommonInterface
{
public string SomeCommonProperty
{
get;set;
}
}
public class BB : ICommonInterface
{
public string SomeCommonProperty
{
get;
set;
}
}
then you can write your generic function as;
public void Test<T>(string a, T arg) where T : ICommonInterface
{
//do something
}
As old as this question is I still get random upvotes on my explanation above. The explanation still stands perfectly fine as it is, but I'm going to answer a second time with a type that's served me well as a substitute for union types (the strongly-typed answer to the question that's not directly supported by C# as is).
using System;
using System.Diagnostics;
namespace Union {
[DebuggerDisplay("{currType}: {ToString()}")]
public struct Either<TP, TA> {
enum CurrType {
Neither = 0,
Primary,
Alternate,
}
private readonly CurrType currType;
private readonly TP primary;
private readonly TA alternate;
public bool IsNeither => currType == CurrType.Neither;
public bool IsPrimary => currType == CurrType.Primary;
public bool IsAlternate => currType == CurrType.Alternate;
public static implicit operator Either<TP, TA>(TP val) => new Either<TP, TA>(val);
public static implicit operator Either<TP, TA>(TA val) => new Either<TP, TA>(val);
public static implicit operator TP(Either<TP, TA> #this) => #this.Primary;
public static implicit operator TA(Either<TP, TA> #this) => #this.Alternate;
public override string ToString() {
string description = IsNeither ? "" :
$": {(IsPrimary ? typeof(TP).Name : typeof(TA).Name)}";
return $"{currType.ToString("")}{description}";
}
public Either(TP val) {
currType = CurrType.Primary;
primary = val;
alternate = default(TA);
}
public Either(TA val) {
currType = CurrType.Alternate;
alternate = val;
primary = default(TP);
}
public TP Primary {
get {
Validate(CurrType.Primary);
return primary;
}
}
public TA Alternate {
get {
Validate(CurrType.Alternate);
return alternate;
}
}
private void Validate(CurrType desiredType) {
if (desiredType != currType) {
throw new InvalidOperationException($"Attempting to get {desiredType} when {currType} is set");
}
}
}
}
The above class represents a type that can be either TP or TA. You can use it as such (the types refer back to my original answer):
// ...
public static Either<FishingBot, ConcreteMixer> DemoFunc(Either<JumpRope, PiCalculator> arg) {
if (arg.IsPrimary) {
return new FishingBot(arg.Primary);
}
return new ConcreteMixer(arg.Secondary);
}
// elsewhere:
var fishBotOrConcreteMixer = DemoFunc(new JumpRope());
var fishBotOrConcreteMixer = DemoFunc(new PiCalculator());
Important Notes:
You'll get runtime errors if you don't check IsPrimary first.
You can check any of IsNeither IsPrimary or IsAlternate.
You can access the value through Primary and Alternate
There are implicit converters between TP/TA and Either<TP, TA> to allow you to pass either the values or an Either anywhere where one is expected. If you do pass an Either where a TA or TP is expected, but the Either contains the wrong type of value you'll get a runtime error.
I typically use this where I want a method to return either a result or an error. It really cleans up that style code. I also very occasionally (rarely) use this as a replacement for method overloads. Realistically this is a very poor substitute for such an overload.
I am building a simple Guard API to protect against illegal parameters being passed to functions and so on.
I have the following code:
public static class Guard
{
public static GuardArgument<T> Ensure<T>(T value, string argumentName)
{
return new GuardArgument<T>(value, argumentName);
}
}
public class GuardArgument<T>
{
public GuardArgument(T value, string argumentName)
{
Value = value;
Name = Name;
}
public T Value { get; private set; }
public string Name { get; private set; }
}
// Example extension for validity checks
public static GuardArgument<T> IsNotNull<T>(this GuardArgument<T> guardArgument, string errorMessage)
{
if (guardArgument.Value == null)
{
throw new ArgumentNullException(guardArgument.Name, errorMessage);
}
return guardArgument;
}
At the moment the code can be used in a similar way to (note this is just a dumb example):
void DummyMethod(int? someObject) {
Guard.Ensure(someObject, "someObject")
.IsNotNull()
.IsGreaterThan(0)
.IsLessThan(10);
}
This all works fine. What I want to be able to do now is extend the API to include child properties in the checks in the following way:
Guard.Ensure(someObject, "someObject")
.IsNotNull()
.Property(
(x => x.ChildProp1, "childProp1")
.IsNotNull()
.IsGreaterThan(10)
)
.Property(
(x => x.ChildProp2, "childProp2")
.IsNotNull()
.IsLessThan(10)
);
Obviously the new .Property method needs to return the parent GuardArgument in order to chain. Furthermore the child property needs to be able to use the existing check methods (IsNotNull() etc) to avoid code duplication.
I cannot work out how to construct the lambda/Property function parameters or where the .Property method should be located - i.e. should it be a property on the GuardArgument or somewhere else, or even if there is a better structure to the API.
The following function allows for a similar syntax to what you want.
public static GuardArgument<T> Property<T, TProp>(this GuardArgument<T> guardArgument, Func<T, TProp> getProperty, string propertyName, Action<GuardArgument<TProp>> validate)
{
GuardArgument<TProp> propertyGuardArgument = new GuardArgument<TProp>(getProperty(guardArgument.Value), propertyName);
validate(propertyGuardArgument);
return guardArgument;
}
The function creates a new GuardArgument for the selected property and then passes this into the Action parameter to allow you to validate as you wish.
This also allows infinite chaining of properties, although I'm not sure that would be particularly readable.
Usage:
Guard.Ensure(someObject, "someObject")
.IsNotNull()
.Property(x => x.ChildProp1, "childProp1", childProp1 =>
childProp1.IsNotNull()
.IsLessThan(10)
.Property(y => y.InnerChildProperty, "innerChildProperty", innerChildProperty =>
innerChildProperty.IsNotNull()
)
)
.Property(x => x.ChildProp2, "childProp2", childProp2 =>
childProp2.IsNotNull()
.IsGreaterThan(10)
);
I think you have no benefit from putting the property checks in the chain of the parent object checks. So I would recommend to make one chain for the parent object and for each property another chain. This is much more readable:
Guard.Ensure(a, "a")
.IsNotNull("a is null");
Guard.Ensure(a.p0, "a.p0")
.IsGreaterThan(10);
Guard.Ensure(a.p1, "a.p1")
.IsGreaterThan(5);
I think you are reinventing a wheel here. Install this extension - Code Contracts and here is docs how to use it.
In addition to code based asserts similar to yours, i.e. :
public int[] Bar(){
Contract.Ensures( Contract.ForAll(0, Contract.Result<int[]>().Length, index => Contract.Result<int[]>()[index] > 0));
....
}
or
Contract.Requires<ArgumentNullException>( x.Value.NestedObject != null, ”x.Value.NestedObject” );
But also has attributes and extensive set of functions for checking interfaces, nice pre- and post- conditions etc. check it out!
Can someone help me figure out how to implement this method generically? The compiler complains that it cannot resolve t.Id. Which makes sense but, how do I tell it that all objects that pass will have an Id property. Here is the interface I defined for T:
namespace LiveWire.Model
{
public interface ILiveWireModel
{
Guid Id { get; }
}
}
The interface for all repositories:
internal interface ILiveWireRepository<T>
{
ICacheProvider Cache { get; }
string CacheKey { get; }
SqlConnection CreateConnection();
IEnumerable<T> GetData<TD>();
IEnumerable<T> LoadData<TD>();
Dictionary<Guid, T> GetCachedData<TD>();
void ClearCache();
}
And my method:
public IEnumerable<T> GetData<TD>()
where TD : ILiveWireModel
{
var data = GetCachedData<TD>();
if (data == null)
{
data = LoadData<TD>().ToDictionary(t => t.Id);
if (data.Any())
{
Cache.Set(CacheKey, data, 30);
}
}
return data.Values;
}
I'm including the whole class here which I hope will clear some things up.
internal abstract class LiveWireRepositoryBase<T> : ILiveWireRepository<T>
{
public ICacheProvider Cache { get; private set; }
public string CacheKey { get; private set; }
internal LiveWireRepositoryBase()
{
Cache = new DefaultCacheProvider();
}
public SqlConnection CreateConnection()
{
return new SqlConnection(
ConfigurationManager
.ConnectionStrings["LiveWire4Database"]
.ConnectionString);
}
public IEnumerable<T> GetData<TD>()
where TD : ILiveWireModel
{
var data = GetCachedData<TD>();
if (data == null)
{
data = LoadData<TD>().ToDictionary(t => t.Id);
if (data.Any())
{
Cache.Set(CacheKey, data, 30);
}
}
return data.Values;
}
public IEnumerable<T> LoadData<TD>()
{
return new List<T>();
}
public Dictionary<Guid, T> GetCachedData<TD>()
{
throw new NotImplementedException();
}
public void ClearCache()
{
throw new NotImplementedException();
}
}
I'm getting this error which I don't understand. I tried using an explicit interface implementation but, that wound up making me remove my where constraint.
The constraints for type parameter 'TD' of method 'LiveWire.Data.Repositories.LiveWireRepositoryBase.GetData()' must match the constraints for type parameter 'TD' of interface method 'LiveWire.Data.Repositories.ILiveWireRepository.GetData()'. Consider using an explicit interface implementation instead. C:\projects\LiveWire\Solution\LiveWire.Data\Repositories\LiveWireRepositoryBase.cs 32 31 LiveWire.Data
You'll be able to make this method compile by changing the class's signature to
public sealed class MyCache<T> where T : ILiveWireModel
(or, if the class is in a different namespace, where T : LiveWire.Model.ILiveWireModel).
That said, I'm not sure that this change will solve your problem. I have only seen a few snippets of your project's code, so I may be wrong, and take the following with a grain of salt:
Is it really the best design to keep GUID-keyed and integer-keyed values in the same cache? Presumably, you're taking data from two different sources, one which uses GUID keys and one which uses integer keys. But in the future, what if you add a third source, which also uses integer keys? The keys from the two integer-key sources could clash, and your cache would always be wrong for some queries. Personally, I'd maintain a second table or function (maybe keep a table of mappings for integer-valued keys, just pass through the GUID-valued keys) somewhere that knows the mapping from objects to keys, and use that function whenever I need to check if an object is cached. All the rest of the time, then, your cache could work directly in terms of keys and values, and not have to mess with different types of keys.
The exception you get at this stage just says that the interface definition
IEnumerable<T> GetData<TD>();
hasn't got the same constraints (i.e. the where) for the type parameter TD as the implementation
public IEnumerable<T> GetData<TD>() where TD : ILiveWireModel
You need to put the same constraint in the interface.
public IEnumerable<T> GetData<T>() where T:LiveWire.Model.ILiveWireModel {
//.../
}
Specialization of generics.
You need to fix the declaration first
public IEnumerable<T> GetData<T>()
then, in order to know what you can use ON T, you have to tell it what T is allowed to be.
public IEnumerable<T> GetData<T>() where T : ILiveWireModel
Finally, you haven't told us what var data actually contains, that would be inside of the GetCachedData and the LoadData Functions, which you dont pass T into and we dont have any idea what it returns.
I would expect to see something like this though
public IEnumerable<T> GetData<T>() where T : ILiveWireModel
{
var data = GetCachedData<T>();
if (data == null)
{
data = LoadData<T>().ToDictionary(t => t.Id);
if (data.Any())
{
Cache.Set(CacheKey, data, 30);
}
}
return data.Values;
}
Reading this, I learned it was possible to allow a method to accept parameters of multiple types by making it a generic method. In the example, the following code is used with a type constraint to ensure "U" is an IEnumerable<T>.
public T DoSomething<U, T>(U arg) where U : IEnumerable<T>
{
return arg.First();
}
I found some more code which allowed adding multiple type constraints, such as:
public void test<T>(string a, T arg) where T: ParentClass, ChildClass
{
//do something
}
However, this code appears to enforce that arg must be both a type of ParentClass and ChildClass. What I want to do is say that arg could be a type of ParentClass or ChildClass in the following manner:
public void test<T>(string a, T arg) where T: string OR Exception
{
//do something
}
Your help is appreciated as always!
That is not possible. You can, however, define overloads for specific types:
public void test(string a, string arg);
public void test(string a, Exception arg);
If those are part of a generic class, they will be preferred over the generic version of the method.
Botz answer is 100% correct, here's a short explanation:
When you are writing a method (generic or not) and declaring the types of the parameters that the method takes you are defining a contract:
If you give me an object that knows how to do the set of things that
Type T knows how to do I can deliver either 'a': a return value of the
type I declare, or 'b': some sort of behavior that uses that type.
If you try and give it more than one type at a time (by having an or) or try to get it to return a value that might be more than one type that contract gets fuzzy:
If you give me an object that knows how to jump rope or knows how to calculate pi
to the 15th digit I'll return either an object that can go fishing or maybe mix
concrete.
The problem is that when you get into the method you have no idea if they've given you an IJumpRope or a PiFactory. Furthermore, when you go ahead and use the method (assuming that you've gotten it to magically compile) you're not really sure if you have a Fisher or an AbstractConcreteMixer. Basically it makes the whole thing way more confusing.
The solution to your problem is one of two possiblities:
Define more than one method that defines each possible transformation, behavior, or whatever. That's Botz's answer. In the programming world this is referred to as Overloading the method.
Define a base class or interface that knows how to do all the things that you need for the method and have one method take just that type. This may involve wrapping up a string and Exception in a small class to define how you plan on mapping them to the implementation, but then everything is super clear and easy to read. I could come, four years from now and read your code and easily understand what's going on.
Which you choose depends on how complicated choice 1 and 2 would be and how extensible it needs to be.
So for your specific situation I'm going to imagine you're just pulling out a message or something from the exception:
public interface IHasMessage
{
string GetMessage();
}
public void test(string a, IHasMessage arg)
{
//Use message
}
Now all you need are methods that transform a string and an Exception to an IHasMessage. Very easy.
If ChildClass means it is derived from ParentClass, you may just write the following to accept both ParentClass and ChildClass;
public void test<T>(string a, T arg) where T: ParentClass
{
//do something
}
On the otherhand, if you want to use two different types with no inheritance relation between them, you should consider the types implementing the same interface;
public interface ICommonInterface
{
string SomeCommonProperty { get; set; }
}
public class AA : ICommonInterface
{
public string SomeCommonProperty
{
get;set;
}
}
public class BB : ICommonInterface
{
public string SomeCommonProperty
{
get;
set;
}
}
then you can write your generic function as;
public void Test<T>(string a, T arg) where T : ICommonInterface
{
//do something
}
As old as this question is I still get random upvotes on my explanation above. The explanation still stands perfectly fine as it is, but I'm going to answer a second time with a type that's served me well as a substitute for union types (the strongly-typed answer to the question that's not directly supported by C# as is).
using System;
using System.Diagnostics;
namespace Union {
[DebuggerDisplay("{currType}: {ToString()}")]
public struct Either<TP, TA> {
enum CurrType {
Neither = 0,
Primary,
Alternate,
}
private readonly CurrType currType;
private readonly TP primary;
private readonly TA alternate;
public bool IsNeither => currType == CurrType.Neither;
public bool IsPrimary => currType == CurrType.Primary;
public bool IsAlternate => currType == CurrType.Alternate;
public static implicit operator Either<TP, TA>(TP val) => new Either<TP, TA>(val);
public static implicit operator Either<TP, TA>(TA val) => new Either<TP, TA>(val);
public static implicit operator TP(Either<TP, TA> #this) => #this.Primary;
public static implicit operator TA(Either<TP, TA> #this) => #this.Alternate;
public override string ToString() {
string description = IsNeither ? "" :
$": {(IsPrimary ? typeof(TP).Name : typeof(TA).Name)}";
return $"{currType.ToString("")}{description}";
}
public Either(TP val) {
currType = CurrType.Primary;
primary = val;
alternate = default(TA);
}
public Either(TA val) {
currType = CurrType.Alternate;
alternate = val;
primary = default(TP);
}
public TP Primary {
get {
Validate(CurrType.Primary);
return primary;
}
}
public TA Alternate {
get {
Validate(CurrType.Alternate);
return alternate;
}
}
private void Validate(CurrType desiredType) {
if (desiredType != currType) {
throw new InvalidOperationException($"Attempting to get {desiredType} when {currType} is set");
}
}
}
}
The above class represents a type that can be either TP or TA. You can use it as such (the types refer back to my original answer):
// ...
public static Either<FishingBot, ConcreteMixer> DemoFunc(Either<JumpRope, PiCalculator> arg) {
if (arg.IsPrimary) {
return new FishingBot(arg.Primary);
}
return new ConcreteMixer(arg.Secondary);
}
// elsewhere:
var fishBotOrConcreteMixer = DemoFunc(new JumpRope());
var fishBotOrConcreteMixer = DemoFunc(new PiCalculator());
Important Notes:
You'll get runtime errors if you don't check IsPrimary first.
You can check any of IsNeither IsPrimary or IsAlternate.
You can access the value through Primary and Alternate
There are implicit converters between TP/TA and Either<TP, TA> to allow you to pass either the values or an Either anywhere where one is expected. If you do pass an Either where a TA or TP is expected, but the Either contains the wrong type of value you'll get a runtime error.
I typically use this where I want a method to return either a result or an error. It really cleans up that style code. I also very occasionally (rarely) use this as a replacement for method overloads. Realistically this is a very poor substitute for such an overload.