开发者

Creating a generic typed member variable without type parameters specified on the class

开发者 https://www.devze.com 2023-04-01 16:32 出处:网络
I apologise for the unclear title, I don\'t really know how else to say it. I\'ll demonstrate with code.

I apologise for the unclear title, I don't really know how else to say it. I'll demonstrate with code.

public class CalculationService
{
    private static Calculator<StaticInstrument, StaticResult> _staticCalculator;
    private static Calculator<DynamicInstrument, DynamicResult> _dynamicCalculator;
    private static readonly object IsInitializedLock = new object();

    public byte[] ProcessStatic()
    {
        lock (IsInitializedLock)
        {
            _staticCalculator = new Calculator<StaticInstrument, StaticResult>();
        }

        return _staticCalculator.Calculate();
    }

    public byte[] ProcessDynamic()
    {
        lock (IsInitializedLock)
        {
            _dynamicCalculator = new Calculator<DynamicInstrument, DynamicResult>();
        }

        return _dynamicCalculator.Calculate();
    }
}

I have many methods that are identical, except for the fact that the type parameters of the Calculator objects are different.

The problem is, because CalculationService is a WCF service, it cannot have any type parameters. For this reason I can't simpl开发者_JAVA百科y create a static member Calculator<TInstrument, TResult>, because I cannot put type parameters on CalculationService.

My ideal solution would be something like this:

public class CalculationService
{
    private static Calculator<TInstrument, TResult> _calculator;
    private static readonly object IsInitializedLock = new object();

    public byte[] Process<TInstrument, TResult>()
    {
        lock (IsInitializedLock)
        {
            _calculator = new Calculator<TInstrument, TResult>();
        }

        return _calculator.Calculate();
    }

    public byte[] ProcessStatic()
    {
        return Process<StaticInstrument, StaticResult>();
    }

    public byte[] ProcessDynamic()
    {
        return Process<DynamicInstrument, DynamicResult>();
    }
}

But I can't create that private static Calculator<TInstrument, TResult> _calculator member without specifying type information on CalculationService, which I cannot do with WCF.

The only way I can think to do this would be to have

private static dynamic _calculator;

Is there any other way? I would rather avoid using dynamic if possible, I'm not sure what the performance implications could be, if any.


I don't think I understand your later comment about having "just one instance of Calculator on my CalculationService class"; rather, it seems to me that you need one instance per <TInstrument,TResult>, as in the code you posted originally. I think the below code will do what you want (with the added bonus that the static initialization rules will thread-safely construct the Calculator at the right time).

public class CalculationService {
  private static class Inner<TInstrument, TResult> {
    public static readonly Calculator<TInstrument, TResult> _calculator;

    static Inner() {
      _calculator=new Calculator<TInstrument, TResult>();
    }
  }

 public byte[] ProcessStatic() {
    return Inner<StaticInstrument, StaticResult>._calculator.Calculate();
  }

  public byte[] ProcessDynamic() {
    return Inner<DynamicInstrument, DynamicResult>._calculator.Calculate();
  }
}


You could "burn in" the types using inheritance to keep WCF from complaining.

public class StaticCalculator:Calculator<StaticInstrument, StaticResult> {}
public class DynamicCalculator:Calculator<DynamicInstrument, DynamicResult> {}

public class CalculationService
{
    private static StaticCalculator _staticCalculator;
    private static DynamicCalculator _dynamicCalculator;
    ...

I've done this before to work around WCF's lack of support for generic type parameters.


if you only want 1 version of your service, you cannot have a single _calculator variable. The "easy" way is to use some version of a lookup structure (like dictionary, etc.) and lookup the proper calculator. Use the combination of TInstrument and TResult as the key to your calculator.

You can then use the typeof operator to get your keys. Something like:

_calculators = new Dictionary<Type,<Dictionary<Type,Calculator<TInstrument, TResult>>>();
...
var key1 = typeof(TInstrument);
var key2 = typeof(TResult);
var calculator = _calculators[key1].[key2];
...

I know this may seem complicated because of the generics syntax for creating a generic dictionary of dictionaries of calculators... but it works.

Hope this helps,

Eric.

0

精彩评论

暂无评论...
验证码 换一张
取 消