The typical scenario is as follows.

class ServiceBase
{
    public virtual InstanceBase GetObject() { return null; }
}

class ServiceA : ServiceBase
{
    public override InstanceBase GetObject()
    {
        var v = new InstanceA();
        v.Method();
        return v;
    }
}

class ServiceB : ServiceBase
{
    public override InstanceBase GetObject()
    {
        var v = new InstanceB();
        v.Method();
        return v;
    }
}

class InstanceBase
{
    public virtual void Method() { }
}

class InstanceA : InstanceBase
{
    public override void Method(){}
}

class InstanceB : InstanceBase
{
    public override void Method() { }
}

Different services deal with different Instance Classes. But the code for GetObject is similar for all services. I want to reduce the code base by extending base class' GetObject method that performs the most.

class ServiceBase
{
     public virtual InstanceBase GetObject<T>()
     {
         var v= (InstanceBase)Activator.CreateInstance(typeof(T), new object[] { });
         v.Method();
         return v;
     }
}

This seems to be a way to achieve. However I am little wary to use reflection as it may run into performance issues. Is there a better way to achieve this?

有帮助吗?

解决方案

Yes there are, a couple of ways:

1) Generic approach:

class ServiceBase<T> where T : InstanceBase, new()
{
    public InstanceBase GetObject() //you can make the return type even 'T'
    {
        var v = new T();
        v.Method();
        return v;
    }
}

class ServiceA : ServiceBase<InstanceA> 
{

}

class ServiceB : ServiceBase<InstanceB> 
{

}

This is just better since it has the least code duplication, but I'm not sure if the genericity is going to be a hassle ever.

2) If that doesn't fit, you can ask the base class to provide their own InstanceBase. This looks cleaner and simpler. Like:

abstract class ServiceBase
{
    public abstract InstanceBase Instance { get; }

    public InstanceBase GetObject() //you can make the return type even 'T'
    {
        Instance.Method();
        return Instance;
    }
}

class ServiceA : ServiceBase 
{
    public override InstanceBase Instance { get; } //return new InstanceA() here
}

class ServiceB : ServiceBase
{
    public override InstanceBase Instance { get; } //return new InstanceB() here
}

Now from the overridden property Instance, return an new instance of InstanceBase or an already instantiated instance. That depends on your logic, but from the shown method it seems, you got to return a new instance every time.


Which suits you depends on your context. Whatever it is, think about:

1) This approach is bad.

 public virtual InstanceBase GetObject<T>()
 {
     var v= (InstanceBase)Activator.CreateInstance(typeof(T), new object[] { });
     v.Method();
     return v;
 }

For one, you have to specify the type argument whenever you're calling the function, two, there is every possibility that one can mess things up.

ServiceA a = new ServiceA();
a.GetObject<InstanceB>(); // not probably what you want.

2) In any case, you can give a new() constraint for a generic type argument T if you're going for it.

3) You need not specify empty parameter collection if you're to instantiate with the default constructor. This would do:

var v= (InstanceBase)Activator.CreateInstance(typeof(T));
// or just new T();

4) You seem to not have used any instance members at all in your GetObject method. If that is the case, then you can have a static method. Whether this should be static or non-static again depends, depending on the call you would want to make. Just have static in mind.

public static InstanceBase GetObject() //you can make the return type even 'T'
{
    var v = new T();
    v.Method();
    return v;
}

//can call like this too:
ServiceA.GetObject(); //etc

其他提示

Just specify that T is class that can be instancied:

public virtual InstanceBase GetObject<T>() where T : class, new()

You can then instanciate your object without using reflection:

T myInstance = new T();

There will definitely be a hit in performance. Consider the following code:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Create 1MM ServiceA...");

        var sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < 1000000; i++)
        {
            var o = new ServiceA();
            var obj = o.GetObject();
        }
        sw.Stop();

        Console.WriteLine("{0} ms", sw.ElapsedMilliseconds);
        Console.WriteLine();

        Console.WriteLine("Create 1MM ServiceB...");

        sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < 1000000; i++)
        {
            var o = new ServiceB();
            var obj = o.GetObject();
        }
        sw.Stop();

        Console.WriteLine("{0} ms", sw.ElapsedMilliseconds);
        Console.WriteLine();

        Console.WriteLine("Create 1MM GenericServiceA...");

        sw= new Stopwatch();
        sw.Start();
        for (int i = 0; i < 1000000; i++)
        {
            var o = new GenericServiceBase();
            var obj = o.GetObject<ServiceA>();
        }
        sw.Stop();

        Console.WriteLine("{0} ms", sw.ElapsedMilliseconds);
        Console.WriteLine();

        Console.WriteLine("Create 1MM GenericServiceB...");

        sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < 1000000; i++)
        {
            var o = new GenericServiceBase();
            var obj = o.GetObject<ServiceB>();
        }
        sw.Stop();

        Console.WriteLine("{0} ms", sw.ElapsedMilliseconds);
        Console.WriteLine();
    }
}

class GenericServiceBase
{
    public virtual InstanceBase GetObject<T>()
        where T : ServiceBase
    {
        var v = (ServiceBase)Activator.CreateInstance(typeof(T), new object[] { });
        return v.GetObject();
    }
}

class ServiceBase
{
    public virtual InstanceBase GetObject() { return null; }
}

class ServiceA : ServiceBase
{
    public override InstanceBase GetObject()
    {
        var v = new InstanceA();
        v.Method();
        return v;
    }
}

class ServiceB : ServiceBase
{
    public override InstanceBase GetObject()
    {
        var v = new InstanceB();
        v.Method();
        return v;
    }
}

class InstanceBase
{
    public virtual void Method() { }
}

class InstanceA : InstanceBase
{
    public override void Method() { }
}

class InstanceB : InstanceBase
{
    public override void Method() { }
}

and its output:

Create 1MM ServiceA...
34 ms

Create 1MM ServiceB...
33 ms

Create 1MM GenericServiceA...
1226 ms

Create 1MM GenericServiceB...
1255 ms

UPDATE: if you put in the little trick with the new keyword on the constraint, you get the following results:

Create 1MM ServiceA...
35 ms

Create 1MM ServiceB...
32 ms

Create 1MM GenericServiceA...
182 ms

Create 1MM GenericServiceB...
177 ms

The performance hit over 1MM isn't near as bad, but it's still there.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top