C# generic typing with multiple class constraints - c#

TL;DR
I'd like this to compile in C#
public void some_method< T >() where T : class1, class2
Is this possible?
Full Context
I have two methods that are identical except for one parameter.
public SignInResponseMessage Generate(SignInRequestMessage request, (X509Certificate2 || WindowsPrincipal) principal, Uri requestUri)
{
SignInResponseMessage response = null;
ClaimsIdentity identity = null;
if (principal != null)
{
identity = CreateSubject(principal);
response = Generate(request, requestUri, identity);
}
else
{
throw new ArgumentNullException("principal");
}
return response;
}
I'm currently replicating this method and it's making me cringe a little inside as I would really like to make this DRY-er. Looking around, this documentation seemed promising, but it only allows me to add a single class constraint. I get the following error on the second class:
Error 1 The class type constraint 'class2' must come before any other constraints
If WindowsPrincipal and X509Certificate2 were two classes I had written I could easily make them implement the same interface and I would be good to go, but that's not an option.
Is there any way to accomplish what I'd like to do?
If not, I'd like to know more about the underlying mechanism that makes this impossible.

I am afraid that this could lead to issues with working out what method to actually call. Imagine if one of the classes specified inherited from the other and there was an override for that method!?
Please see the "Diamond Problem" for a complete description of the reasons
If you want to work around it. You can set up the common shared interface with an adapter and then use that.
interface IAdaper {
SomeMethod();
}
class AdapterOne : IAdapter {
TypeOneToCall _one;
public AdapterOne (TypeOneToCall one) {
_one = one;
}
public SomeMethod() {
return _one.SomeMethod();
}
}
class AdapterTwo : IAdapter {
TypeTwoToCall _two;
public AdapterTwo (TypeTwoToCall two) {
_two = two;
}
public SomeMethod() {
return _two.SomeMethod();
}
}
class Generic<T> where T : IAdapter {
// Your implementation here.
}

If you pass the method as a parameter, then T can be anything:
public SignInResponseMessage Generate<T>(SignInRequestMessage request,
Func<T, ClaimsIdentity> createSubject,
T principal,
Uri requestUri)
{
SignInResponseMessage response = null;
ClaimsIdentity identity = null;
if (principal != null)
{
identity = createSubject(principal);
response = Generate(request, requestUri, identity);
}
else
{
throw new ArgumentNullException("principal");
}
return response;
}
So to reuse the method:
var r1 = Generate<X509Certificate2>(request, CreateSubject, certificate, uri);
var r2 = Generate<WindowsPrincipal>(request, CreateSubject, principal, uri);

Related

How to use generic class in Xamarin SQLite Project

I'm working on a software that uses a SQLite local database in Xamarin.
I'm using Microsoft's Todo sample as a base.
https://learn.microsoft.com/ja-jp/xamarin/xamarin-forms/data-cloud/data/databases
In this sample, only Todo is stored, so the only class that accesses SQLite is TodoItemDatabase.
In the software we are building, we are planning to access multiple tables such as Todo, Memo, Diary, and so on.
In that case, we need to create TodoItemDatabase, MemoItemDatabase, and DiaryItemDatabase separately for each of them.
So, I decided to use generic classes in this case.
public class BaseDatabase<T>
{
static SQLiteAsyncConnection Database;
public static readonly AsyncLazy<ItemDatabase> Instance = new AsyncLazy<ItemDatabase>(async () =>
{
File.Delete(Constants.DatabasePath);
var instance = new ItemDatabase();
try
{
CreateTableResult result = await Database.CreateTableAsync<T>();
}
catch(Exception exception)
{
var error = exception.Message;
}
return instance;
});
public BaseDatabase()
{
Database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
}
public Task<List<T>> GetItemsAsync()
{
return Database.Table<T>().ToListAsync();
}
public Task<List<T>> GetItemsNotDoneAsync()
{
return Database.QueryAsync<T>("SELECT * FROM [Item] WHERE [Done] = 0");
}
public Task<T> GetItemAsync(string id)
{
return Database.Table<T>().Where(i => i.Id == id).FirstOrDefaultAsync();
}
public Task<int> SaveItemAsync(T item)
{
if (item.Id == null)
{
return Database.InsertAsync(item);
}
else
{
return Database.UpdateAsync(item);
}
}
public Task<int> DeleteItemAsync(T item)
{
return Database.DeleteAsync(item);
}
}
However, when I replaced Task<List> with Task<List> and Task with Task as class BaseDatabase to make it a generic class, two errors occurred.
The first one is that
T must be a generic type or a non-abstract type with a constructor without public parameters to be used as parameter T in method SQLiteAsyncConnection.Table().
T does not contain an Id definition and no accessible extension method ItemId was found that accepts the first argument of type T.
How to solve these two problems?
Please let me know how to solve these two problems in the code of the generic class.
'T' must be a non-abstract type with a public parameterless
constructor in order to use it as parameter 'T' in the generic type or
method 'SQLiteAsyncConnection.CreateTableAsync(CreateFlags)
Just as the tip mentioned,in your code, T must be a generic type or a non-abstract type with a constructor for funtion SQLiteAsyncConnection.CreateTableAsync
without public parameters
CreateTableResult result = await Database.CreateTableAsync<T>();
So, you need use a common class to replace the T here.
we are planning to access multiple tables such as Todo, Memo, Diary,
and so on. In that case, we need to create TodoItemDatabase,
MemoItemDatabase, and DiaryItemDatabase separately for each of them
In a database, you can create many tables inside it instead of creating a database for a every table.
In summary, you can create a table one by one inside of your database.
You can refer to the following code:
public class TodoItemDatabase
{
static SQLiteAsyncConnection Database;
public static readonly AsyncLazy<TodoItemDatabase> Instance = new AsyncLazy<TodoItemDatabase>(async () =>
{
var instance = new TodoItemDatabase();
CreateTableResult result = await Database.CreateTableAsync<TodoItem>();
CreateTableResult result_memo = await Database.CreateTableAsync<Memo>();
CreateTableResult result_diary = await Database.CreateTableAsync<Diary>();
return instance;
});
public TodoItemDatabase()
{
Database = new SQLiteAsyncConnection(Constants.DatabasePath, Constants.Flags);
}
/* query funtion */
public Task<List<TodoItem>> GetItemsAsync()
{
return Database.Table<TodoItem>().ToListAsync();
}
public Task<List<Memo>> GetItemsAsync_memo()
{
return Database.Table<Memo>().ToListAsync();
}
// other code
}

Passing an interface method as a parameter

Note: This could very well be very C# specific language question, unrelated to WCF or web services at all.
There is a 3-party ASMX web service, which shall be used for data retrieval. I have created a generalized method called ExecuteCommand() which is used for every request against the web service. The purpose of this method is to handle cookie session/exceptions and other common logic. For each request a new channel shall be used, in order to simplify the disposal of unused resources.
The problem is that to use the ExecuteCommand() method - I have to initialize a channel each time, in order to be able to pass the method to be executed as an argument. Sorry if it sounds too complicated. Here is a usage example:
string color = "blue";
var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
var cars = WcfHelper.ExecuteCommand(channel, () => channel.GetCars(color));
// channel is null here. Channel was closed/aborted, depending on Exception type.
After ExecuteCommand() is called - channel is already disposed of. The reason why channel object is needed at all, is to be able to provide a method to be executed as a parameter! i.e.() => channel.GetCars(). To further support these words, here is the WcfHelper class internals:
public static class WcfHelper
{
public static Cookie Cookie { get; set; }
public static T ExecuteCommand<T>(IClientChannel channel, Expression<Func<T>> method)
{
T result = default(T);
try
{
// init operation context
using (new OperationContextScope(channel))
{
// set the session cookie to header
if (Cookie != null) {
HttpRequestMessageProperty request = new HttpRequestMessageProperty();
request.Headers["Cookie"] = cookie.ToString();
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = request;
}
// execute method
var compiledMethod = method.Compile();
result = compiledMethod.Invoke();
}
}
// do different logic for FaultException, CommunicationException, TimeoutException
catch (Exception)
{
throw;
}
finally
{
CloseOrAbortServiceChannel(channel);
channel = null;
}
return result;
}
private static void CloseOrAbortServiceChannel(ICommunicationObject communicationObject)
{
bool isClosed = false;
if (communicationObject == null || communicationObject.State == CommunicationState.Closed)
return;
try
{
if (communicationObject.State != CommunicationState.Faulted)
{
communicationObject.Close();
isClosed = true;
}
}
catch (Exception)
{
throw;
}
finally
{
if (!isClosed)
AbortServiceChannel(communicationObject);
}
}
private static void AbortServiceChannel(ICommunicationObject communicationObject)
{
try
{
communicationObject.Abort();
}
catch (Exception)
{
throw;
}
}
}
So the short question - it it possible to initialize a channel variable inside the ExecuteCommand method itself, while having a possibility to define, which method shall be executed inside ExecuteCommand for a given channel?
I am trying to accomplish something like this:
string color = "blue";
var cars = WcfHelper.ExecuteCommand<Car[], CarServiceSoapChannel>(channel => channel.GetCars(color));
or even
string color = "blue";
var cars = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.GetCars(color));
Any other code improvement suggestions are welcomed but not mandatory, of course.
P.S. ASMX is added as a Service reference in Visual Studio. Therefore, there were some entities that automatically generated for the "CarService", such as - CarServiceSoapChannel interface, CarServiceSoapClient class and of course CarService interface containing methods of a web service. In the example above a ChannelFactory is used to create a channel for the CarServiceSoapChannel interface, hence, here is where the question name is coming from: Passing an interface method as a parameter. This could be a bit misleading, but I hope it's clear what I trying to accomplish from the description itself.
Update 25.05.2018
I followed the advice of #nvoigt and was able to achieve the result I wanted:
public static TResult ExecuteCommand<TInterface, TResult>(Func<TInterface, TResult> method)
where TInterface : IClientChannel
{
TResult result = default(TResult);
IClientChannel channel = null;
try
{
channel = StrategyFactory.CreateChannel<TInterface>();
// init operation context
using (new OperationContextScope(channel))
{
// set the session cookie to header
if (Cookie != null)
Cookie.SetCookieForSession();
// execute method
result = method((TInterface)channel);
}
}
catch (Exception)
{
throw;
}
finally
{
CloseOrAbortServiceChannel(channel);
channel = null;
}
return result;
}
This already achieves the initial goal. There is, however, one issue with this approach. In order to call the method - you have to explicitly specify the method's return parameter.
So to say, if you want to call the method - writing this is not enough:
var result = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.IsBlue())
You will have to specifically specify, that the return type shall be boolean
var result = WcfHelper.ExecuteCommand<CarServiceSoapChannel, bool>(channel => channel.IsBlue())
I personally don't mind writing an extra bit of code, as it is still a big advantage over my initial method implementation, however, I am wondering in the new version could be improved?
For example, When I had just 1 generic TResult type in the method - the return type <TResult> could be omitted. This is no longer the case with 2 generics. In any case, thank you #nvoigt!
Your method signature should be:
public static TResult ExecuteCommand<TInterface>(Func<TInterface, TResult> method)
Then in your WcfHelper (which should probably not be static anymore because it needs a member _strategyFactory) you create a channel as before:
{
var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
return method(channel);
}
Obviously, you need to add all the fancy try/finally stuff again.
As you should have instances anyway now with the factory in the class as a member, you could put the service contract generic into your class to make it easier for users:
public class ConnectionToService<TInterface> : where TInterface : class
{
public TResult ExecuteCommand<TResult>(Func<TInterface, TResult> method)
{
var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
return method(channel);
}
}
Usage:
var service = new ConnectionToService<ICarService>();
var color = service.ExecuteCommand(s => s.GetColor());

Return instance using reflection in C#

A sample code I tried to return an instance of class is given below.
public object getConstructorclass(int i)
{
if(i==1)
{
Type type = Type.GetType("test1");
}else
{
Type type = Type.GetType("test2");
}
return Activator.CreateInstance(type);
}
var objcls = getConstructorclass(1);
objcls.callclass();//error occured
How can I mention the class type here since the type is not known at compile time but it will decided at runtime.In the above example i just pass a value 1 (it can be anything and that class will be called accordingly), and the class test1 called.
here I will get an error on the line objcls.callclass(), because objcls is an object instance that doesn't have a callclass()method.
How can I restructure this piece of code? My aim is if I mention a class in the getConstructorclass() method, an object should be returned so as to use it in the further code to invoke the members of that class.
If you know that your classes will have this method, you should use a common interface for them and implement it accordingly. Then you will work with classes that you have made sure it will work.
It would look like this
IMyInterface objcls = getconstrorclass() as IMyInterface;
if (objcls != null)
objcls.callclass();
else
// we failed miserably and should do something about it
I don't think you should use some generic object returning constructor based on an int variable, if your classes don't have anything in common. It's really weird to handle it like this and it may lead to various problems (some of which you're currently already experiencing). Generic class constructors make sense if the classes are somewhat related and you can predict the outcome, but to create a do-it-all method.. Not so sure about correctness of such approach.
Anyway, if you insist (not recommended, but as you wish), you can create some checks for a type like this:
var createdObject = getConstructorclass(1);
if (createdObject is MyClass1)
{
var specificObject = (MyClass1)createdObject;
specificObject.callMethod1();
}
else if (createdObject is MyClass2)
{
var specificObject = (MyClass2)createdObject;
specificObject.callSomeOtherMethod();
}
...
But it gets very error prone soon, refactoring will probably be a nightmare etc., but it's your call..
Or you maybe can use solution from pwas, but to me it seems unnecessarily complicated for such a basic task. Looks nice and all, but it still returns only the type "object", so it doesn't really solve your specific problem.
Also, to address one issue I'm not sure you understand - you've already created the instance, you just return type object. That is why you can't call any specific methods on this object, because first you have to cast it to something, that actually has that method and make sure the cast can be done (inheritance etc).
If interface solution (see other answers) is enough, don't look at this answer. When you can't use common base class / interface and you still want call members, you can use solution with is keyword (and check types). Instead of writing many ifs for each case, you can use fluent API:
object obj = this.getConstructorclass();
obj.StronglyInvoke()
.When<int>(value => Console.WriteLine("Got {0} as int", value))
.When<string>(value => Console.WriteLine("Got {0} as string", value))
.OnFail(() => Debug.Write("No handle."))
.Invoke();
Solution:
public class GenericCaller
{
private IList<GenericInvoker> invokers = new List<GenericInvoker>();
private readonly object target;
private Action failAction;
public GenericCaller(object target)
{
if (target == null)
{
throw new ArgumentNullException("target");
}
this.target = target;
}
public GenericCaller OnFail(Action fail)
{
this.failAction = fail;
return this;
}
public GenericCaller When<T>(Action<T> then)
{
if (then == null)
{
throw new ArgumentNullException("then");
}
var invoker = new GenericInvoker<T>(this.target, then);
this.invokers.Add(invoker);
return this;
}
public void Invoke()
{
if (this.invokers.Any(invoker => invoker.Invoke()))
{
return;
}
if (this.failAction == null)
{
throw new InvalidOperationException("Handler not found");
}
this.failAction();
}
public abstract class GenericInvoker
{
protected readonly object target;
protected GenericInvoker(object target)
{
this.target = target;
}
public abstract bool Invoke();
}
public class GenericInvoker<T> : GenericInvoker
{
private readonly Action<T> then;
public GenericInvoker(object target, Action<T> then)
: base(target)
{
this.then = then;
}
public override bool Invoke()
{
if (this.target.GetType() == typeof(T))
{
this.then((T)this.target);
return true;
}
return false;
}
}
}
public static class Extensions
{
public static GenericCaller StronglyInvoke(this object o)
{
return new GenericCaller(o);
}
}
Remeber - it would be more elegant to use common interface (as other answers say) - my is only alternative way.
Declare your variable as dynamic
dynamic objcls = getconstrorclass();
Using this the will be determined at run-time, whatever the getconstrorclass method returns. You can access any member of the type and you won't get any error at compile-time. But if you try to access a member which doesn't exists you will get a RuntimeBinderException at runtime.
I would recommend using an interface and restricting the classes that you can instantiate this way to only those that implement the interface.
public interface IMyInterface
{
void callclass();
}
public <T> getConstructorClass()
{
T instance;
Type type = Type.GetType("test1");
// instance will be null if the object cannot be cast to type T.
instance = Activator.CreateInstance(type) as T;
return T;
}
IMyInterface objcls = getConstructorClass<IMyInterface>();
if(null != objcls)
{
objcls.callclass();
}
not sure what you want to achieve in the end, but this looks like a job for "Dependency Injection" - here is a nice sample using autofac

Converting generic type to its base and vice-versa

Can someone help me with the conversion I am facing in enclosed code ... I commented the lines of code, where I am having problem. Is this even the right way to achieve this ... what I am trying to do, is forward responses of specified type to provided callback.
EDIT 1
I forgot to mention that Response and AFResponse are abstract classes,
which goes: >Response -> AFResponse -> concrete implementations of AF
layer messages
public class MessageBinder
{
private class Subscriber<T> : IEquatable<Subscriber<T>> where T : Response
{
...
}
private readonly Dictionary<Type, List<Subscriber<Response>>> bindings;
public MessageBinder()
{
this.bindings = new Dictionary<Type, List<Subscriber<Response>>>();
}
public void Bind<TResponse>(short shortAddress, Action<ZigbeeAsyncResponse<TResponse>> callback)
where TResponse : Response
{
List<Subscriber<TResponse>> subscribers = this.GetSubscribers<TResponse>();
if (subscribers != null)
{
subscribers.Add(new Subscriber<TResponse>(shortAddress, callback));
}
else
{
var subscriber = new Subscriber<TResponse>(shortAddress, callback);
// ERROR: cannot convert from 'List<Subscriber<TResponse>>' to 'List<Subscriber<Response>>' ... tried LINQ Cast operator - does not work either
this.bindings.Add(typeof(TResponse), new List<Subscriber<TResponse>> { subscriber });
}
}
public void Forward<TResponse>(TResponse response)
where TResponse : Response
{
var subscribers = this.GetSubscribers<TResponse>();
if (subscribers != null)
{
Subscriber<TResponse> subscriber;
Type responseType = typeof (TResponse);
if (responseType.IsSubclassOf(typeof (AFResponse)))
{
// ERROR: Cannot convert type 'TResponse' to 'AFResponse' ... tried cast to object first, works, but is this the right way?
var afResponse = (AFResponse)response;
subscriber = subscribers.SingleOrDefault(s => s.ShortAddress == afResponse.ShortAddress);
}
else
{
subscriber = subscribers.First();
}
if (subscriber != null)
{
subscriber.Forward(response);
}
}
}
private List<Subscriber<TResponse>> GetSubscribers<TResponse>() where TResponse : Response
{
List<Subscriber<Response>> subscribers;
this.bindings.TryGetValue(typeof(TResponse), out subscribers);
// ERROR: How can I cast List<Subscriber<Response>> to List<Subscriber<TResponse>>?
return subscribers;
}
}
Thank you for any help :)
EDIT 2
I changed the code according to #Bojan answer, and it is working. But
I am curious, why the Dictionary can't hold the base class of all
Response messages? Is there even a way to accomplish that, or did I
just tried to push my head through a wall?
EDIT 3
Now I am facing another problem ... when message arrive, it is
composed of byte array, >which goes to message factory which resolves
and builds it:
public static T Build<T>(Packet packet) where T : Response
{
Type resolvedType;
if (!dependencyMap.TryGetValue(packet.MessageId, out resolvedType))
{
var str = String.Format("Could not resolve message. Message info: CMD0: {0}, CMD1: {1}, MessageID: {2}",
packet.Cmd0, packet.Cmd1, packet.MessageId);
Debug.WriteLine(str);
throw new MessageNotFoundException(str);
}
ConstructorInfo firstConstructor = resolvedType.GetConstructors().First();
return (T) firstConstructor.Invoke(new object[] {packet});
}
Then an OnAsyncResponseReceived(Response response) event handler
is invoked, which then forwards message to the subscriber of this
message, if any. The problem is now that if I subscribe to (sub layer
of response are: AFResponse, SystemResponse, etc...)
SystemResetResponse which is subclass of SystemResponse which is
subclass of Response, that I must cast that response from
Response(base) type all the way to the concrete type, which is
SystemResetResponse in order the Forwarder in MessageBinder can find
subscribers of this message type, and forwards it.
There are many types and casting by hand would be an overkill ... is
there a way around this, or even a better way to design this type of
system?
EDIT 4
I changed a code like this ... is this the right way to do this, is
there any other, better way and over all, am I trying to solve the
problem the right way or is there better way to handle this?
private void OnAsyncResponseReceived(Response response)
{
dynamic resolvedResponse = Convert.ChangeType(response, response.GetType());
messageBinder.Forward(resolvedResponse);
}
Part of your problem is that you are trying to declare your dictionary as having generic subscribers (List<Subscriber<Response>>), while expecting each entry to be of an unrelated type (List<Subscriber<TResponse>>). A solution would be to hide the actual type behind object or IList:
private readonly Dictionary<Type, object> bindings;
private List<Subscriber<TResponse>> GetSubscribers<TResponse>()
where TResponse : Response
{
object subscribers;
bindings.TryGetValue(typeof(TResponse), out subscribers);
return (List<Subscriber<TResponse>>)subscribers;
}
Your Forward method can check for AFResponse and subclasses in a bit easier way, but the cast will have to go through object:
public void Forward<TResponse>(TResponse response)
where TResponse : Response
{
var subscribers = GetSubscribers<TResponse>();
if (subscribers != null)
{
Subscriber<TResponse> subscriber;
var afResponse = response as AFResponse;
if (afResponse != null)
{
subscriber = subscribers.SingleOrDefault(s => s.ShortAddress == afResponse.ShortAddress);
}
else
{
subscriber = subscribers.First();
}
if (subscriber != null)
{
subscriber.Forward(response);
}
}
}
UPDATE
You did not declare your dictionary to hold "base class of all messages". Even though Response is the base class of all responses, Subscriber<Response> and Subscriber<T> are unrelated types. It is somewhat analogous to how List<int> and List<string> are not related, even though both string and int inherit from object.
What you are looking for is called covariance and is only supported by some interfaces.
For example, if you had:
interface ISubscriber<out T> where T:Response
{}
class Subscriber<T> : ISubscriber<T> where T:Response
{}
You could do:
ISubscriber<Response> s = new Subscriber<AFResponse>();
However, List<ISubscriber<Response>> and List<ISubscriber<AFResponse>> would still be unrelated types.
You need to cast each item in subscribers list to Subscriber<TResponse>.
Something like this always works for me -
private List<T> get<T>()
{
List<IEntity> list = new List<IEntity>();
List<T> genericList = new List<T>();
foreach (var item in list)
{
genericList.Add((T)item);
}
return genericList;
}
Hope it helps!!
As well as the answer already provided, the following lines of code...
Type responseType = typeof (TResponse);
if (responseType.IsSubclassOf(typeof (AFResponse)))
Could be replaced with either:
if (response is AFResponse)
or (better)
AFResponse af = response as AFResponse;
if (af != null)
EDIT Updated the code because I didn't realise beforehand that TResponse was a generic. Are you also aware that IsSubclassOf returns false if its a matching type?

C# Avoid Repetitive Code

I am sure someone may have already asked this type of questions before, but I can't seem to find a similar question.
I have something like this:
client = Client.GetInstance(type.objectA));
if (client != null)
{
result += client.SaveObjectA(object);
}
client = Client.GetInstance(type.objectB));
if (client != null)
{
result += client.SaveObjectB(object);
}
client = Client.GetInstance(type.objectC));
if (client != null)
{
result += client.SaveObjectC(object);
}
client = Client.GetInstance(type.objectD));
if (client != null)
{
result += client.SaveObjectD(object);
}
I wanna find a good way to reduce this repetitive code.
Please let me know your good thoughts.
Thank you
*** Additional to what i put in previously
Forgot to mention a very important part
Those methods were generated from a webservice. Here is the interface.
public interface Client
{
string SaveObjectA(object);
string SaveObjectB(object);
string SaveObjectC(object);
string SaveObjectD(object);
}
Sounds like inheritance would do the trick. Let each client inherit from the same Interface consisting of the method
SaveObject(object o);
then you can just write:
if (client!=null)
{
result+=client.SaveObject(object);
}
and the polymorphism selects the correct version of SaveObject, depending on the type of the client.
It depends on where you want to put the responsibility for saving objects, but I can think of a couple of different ways using interfaces or a factory that knows both how to create and save objects, perhaps a combination of the two.
string result = string.Empty;
foreach (var type in new Type[] { type.objectA, type.objectB, type.objectC, type.objectD })
{
var client = Client.GetInstance(type) as IPersistable;
result += client.Save();
}
where each client implements the IPersistable interface which defines a Save() method.
or
string result = string.Empty;
foreach (var type in new Type[] { type.objectA, type.objectB, type.objectC, type.objectD })
{
var client = Client.GetInstance(type);
result += Client.Save( client );
}
where the Client class knows how to Save each type of object it creates.
This can be done the way you want (see below), however, I recommend implementing a generic, reflection-based solution which will work with most object types.
If you must have a different name for each save method
(giving each save method a different name is usually not good design),
use a hashtable (dictionary) for best performance and less repetitive code:
(If you add the hashtable to the using class instead of a static extension class, you will end up with less code).
static ClientExtensions : class
{
private delegate string MyDelegate(IClient, object);
private static Dictionary<Type, MyDelegate> myDictionary
= new Dictionary<Type, MyDelegate>();
/// <summary>Static Contstructor</summary>
static MyExtenderType()
{
myDictionary.Add(typeof(ClientA), SaveObjectAExtensionMethod);
myDictionary.Add(typeof(ClientB), SaveObjectBExtensionMethod);
myDictionary.Add(typeof(ClientC), SaveObjectCExtensionMethod);
myDictionary.Add(typeof(ClientD), SaveObjectDExtensionMethod);
}
// TODO: copy for B, C & D
public static string SaveObjectAExtensionMethod(this IClient client, object obj)
{
return client.SaveObjectA(obj);
}
public static string SaveObject(this IClient client, object obj)
{
MyDelegate dele;
if (this.myDictionary.TryGetValue(typeof(client), out dele))
return dele(client, obj);
throw new NotSupported...
}
}

Categories