Click here to Skip to main content
15,179,246 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
Hi,

The tl:dr part:
This is a second question following my first that can be found here: c# Interfaces and Generic Types[^]

I am working on a "blue sky" project to improve our core factory classes. My end goal is to have duplicated code and methods shared between the worker classes and to make the use of the factory classes more intuitive.

We currently have to pass the return type and the worker type to the factory which will then use reflection to call the worker method. We end up with a factory class that has a method for every possible method that the worker might have (including some that are specific to only 1 worker >_< ).

I would like to have a factory class that acted as, or returned, a specific worker class so we could tell what methods are available through intellisence and not have to look through the worker class ourselves.

A restriction I have to bare in mind is that each layer of the solution has to be as isolated as possible, but only the business logic layer requires a factory.

I am very interested in using generics with my factory that could hopefully take care of any casting. I'm sure I have done something similar in the past but I no longer have access to that code.

The Current implementation of the factory
The Reflection aspect is what is currently in place. The current Factory method Get for example looks like this:
C#
public partial class Factory<T> where T : new()
{
    public static U Get<U>(object item)
    {
        T instance = Activator.CreateInstance<T>();
        object[] parameters = { item };
        return (U)instance.GetType().GetMethod("Get").Invoke(instance, parameters);
    }
}


Maybe that is the best way after all?

Some specific questions:

Is is possible to determine the type of factory from the return type, or do I have to cast it?
C#
//Can I do this
Member m = Factory.Get(ID); // where Factory<type> knows that the type is Member?
//Or does it have to be one of these:
m = Factory<Member>.Get();
m = Factory.Get<Member>(); 

Maybe this shouldn't be my approach. I'm looking for the best way to make the use of our core layers as easy and as intuitive as possible.

Thanks
Posted
Updated 29-Jan-15 1:25am
v3
Comments
   
Do you mean that when you know some ID, you don't know in advance what type will be returned? But the question is: why knowing it? Could you keep all code agnostic the the runtime types? Say, always work with the interface type IModel (or what is it?) as the compile-time member type.

Also, it remains unclear why you pass the type to the factory and what you do with reflection. (Yes, reflection is the part of this topic. Remember its performance cost. Why do you think serialization is reasonably fast? Only because it uses not only reflection, but also System.Reflection.Emit, to emit code on the fly, which is executed much faster on second use of the same types.)

So, you need to explain the scenario, and better from the point of view of application, not its implementation. What's on input and output of what? On what side, when, the type is knows, and when it is not. (And when it should become known, think why.)

Anyway, if you would agree to do casting, why using generics at all? The whole point is to avoid casting...

—SA
Andy Lanng 29-Jan-15 5:47am
   
The return types will have some common properties but each type will be very different overall.
I guess I don't have to use generics, but I was hoping that I could use the generic type to instantiate a specific factory class for that type, e.i. Factory<t> would return a MemberFactory etc.., but now I think about it, each factory would only be able to expose methods generic to Factory<t> >_<

I have updated the question with the current factory get method so the code is clearer
Sergey Alexandrovich Kryukov 29-Jan-15 10:24am
   
Scenario, I need a scenario. When a concrete type appeared? When an instance of it appears? What, logically, defines what type is used when? What is the ID? Does it carry information or type (type tag) or not? What its role?
—SA
Andy Lanng 29-Jan-15 12:28pm
   
I feel that my terminology is letting me down here.

I want a factory class that when used as "var v = Factory.get(id)" will not compile but where "Member m = Factory.Get(id)" will. I have seen some linq queries that can determine the return time only when the type of the variable being populated is specified.
I am looking for a way to include the return type as part of the signature so that Member m = factory.get(int) will use the "Member Ifactory<member>.Get(int) method and Role r = factory.get(int) will use the "Role Ifactory<role>.Get(int) method. I want the factory to expose these methods publicly. That factory can then call specific workers to do the work but I want a central factory class that can expose the worker methods in the easiest way possible. I don't mean easy as in simple per say, but so that it's really easy to use in the front end code.

This may not be possible. If you can think of a similar way to get the same effect, that would be great, even if I have to specify the worker type. I would Rather keep the worker types protected, though and just expose the model types and the factory.

Forgive me if I still don't make sense. I learned programming autodidactically and I don't often have to communicate my code :/ - sorry 'bout that
Sergey Alexandrovich Kryukov 29-Jan-15 14:43pm
   
Okay, this is not terminology at all. Terminology is fine. This is 1) the level of detail and also 2) the lack of abstraction from your current ideas. If you ask "how to do this and that", you mix-in some portion if "how", the way you presently look at it, to the description of what is "this and that". We all tend to do so.

First of all, get rid of "var", for description purpose. Why? Because this is just syntactic sugar which does not exist in IL. "var v = " will always compile, because it's a strict equivalent of SomeType = SomeMethod(/*...*/), where someType is the return type of SomeMethod. You can think of it in this way: "var" does not exist; it is replaced with someType by some kind of preprocessor.

So, you can forget this part of the idea. You should get back to the scenario in terms of ultimate purposes, getting rid of your "how" ideas as much as possible.

—SA

1 solution

I'm not sure I understand exactly what you're trying to do so this code may not be helpful but I'll take a stab at it.

First, if your generic type constraint ensures that type T has a parameterless constructor you should use new T() instead of Activator.CreateInstance().

Secondly, it seems like you're trying to instantiate type T and then return an property of that type. That seems like an odd use-case to me as the instance of T is then essentially discarded, but assuming that is what you're wanting to do you could do so like this:

C#
public partial class Factory<T> where T : new()
{
    public static U Get<U>(Func<T, U> selector)
    {
        var instance = new T();
        return selector(instance);
    }
}

// use like this:
Factory<Member>.Get(m => m.FirstName);


Again, I'm not sure that is what you're asking for but it seems like it is. You could go further if you want to ensure a specific subclass or interface type on the Get method with a generic type constraint on type U. For instance where U : IWorker.

This approach gives you a single method with intellisense for selecting the members of the factory type, but as I've said it doesn't seem very useful in its current state.


------------------ Update based on closer review of the question -----------------

I think that the following code might be more what you're looking for. If it is I'd suggest that you consider, based on what you're using it for, either switching to a Dependency Injection / Inversion of control framework like Autofac or StructureMap if you're trying to instantiate and return non-data objects or to a data framework like EntityFramework or NHibernate if you are trying to return data from a datastore on demand.

Nevertheless... here is my code which I've not actually checked to see if it will compile:

C#
// type agnostic factory interface (useful for storing instances of various types in a dictionary or list)
public interface IFactory
{
    object Get(int entityId);
}

// generic factory interface
public interface IFactory<T> : IFactory
{
    T Get(int entityId);
}

// abstract base class for factory types that marries the type agnostic and generic interfaces
public abstract class Factory<T> : IFactory<T>
{
    abstract T Get(int entityId);

    object IFactory.Get(int entityId) {
        return Get(entityId);
    }
}

// specialized factory that knows about a specific type
public class MemberFactory : Factory<Member>
{
    public override Member Get(int entityId) {
        return new Member { MemberId = entityId, FirstName = "John", LastName = "Doe" };
    }
}

// generic factory that constructs a value of type T and returns it
public class GenericFactory<T> : Factory<T>
{
    private Func<int, T> constructor;
    public GenericFactory(Func<int, T> constructor)
    {
        this.constructor = constructor;
    }

    public override T Get(int entityId) {
        return constructor(entityId);
    }
}

// non-generic static Factory class which acts as a registry for the different factory instances you will use.
public static class Factory
{
    private static ConcurrentDictionary<Type, IFactory> TypeFactories = new ConcurrentDictionary<Type, IFactory>();

    public static void RegisterFactory<T>(IFactory<T> factory)
    {
        TypeFactories.AddOrUpdate(typeof(T), t => factory, (t, f) => factory);
    }

    public static T Get<T>(int entityId)
    {
        var type = typeof(T);
        IFactory untypedFactory
        if (!TypeFactories.TryGetValue(type, out untypedFactory))
        {
            throw new FactoryNotFoundException(type);
        }

        var factory = untypedFactory as IFactory<T>;
        if (factory == null)
        {
            throw new StopMessingWithMyPrivateStaticDictionaryException();
        }

        return factory.Get(entityId);
    }
}

// application setup:
Factory.RegisterFactory(new MemberFactory());
Factory.RegisterFactory(new GenericFactory<Product>(id => new Product { ProductId = id }));
Factory.RegisterFactory(new GenericFactory<FooBar>(id => new FooBar(id));

// usage:
var member = Factory.Get<Member>(1234);
   
v3

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)




CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900