没有基类问题,如何在这种特殊情况下使用Castle.DynamicProxy Mixin?

时间:2010-11-02 15:42:40

标签: c# .net reflection castle-dynamicproxy

我有一个第三方设计糟糕的图书馆,我必须使用它 它有各种类型,我们称之为 SomeType1 SomeType2 等。 这些类型中没有一个共享一个公共基类,但都有一个名为Value的属性,具有不同的返回类型 我想要做的就是能够在这个类中使用Mixin,这样我就可以调用someType1Instance.ValuesomeType2Instance.Value而无需关心它是什么样的混合类型而不关心返回类型是什么(我可以使用object) 所以我的代码目前是:

public interface ISomeType<V>
{
  V Value {get; set;}
}

public interface ISomeTypeWrapper
{
  object Value { get; set; }
}

public class SomeTypeWrapper<T> : ISomeTypeWrapper
    where T : ISomeType<???>
{
  T someType;

  public SomeTypeWrapper(T wrappedSomeType)
  {
    someType = wrappedSomeType
  }

  public object Value
  {
     get { return someType.Value; }
     set { someType.Value = value != null ? value : default(T); }
  }
}

public class SomeType1
{
  public int Value { get; set; }
}

public class SomeType2
{
  public string Value { get; set; }
}

问题是我不知道在运行之前T可能是什么,因为我得到了一个对象字典。

我可以迭代字典并使用反射在运行时创建SomeWrapperType但我想避免它。

如何将SomeType的concreate类型混合到ISomeType?
我怎么知道V型参数是什么? (希望我有类似于c ++的typedef和decltype)

我怎样才能尽可能少地使用反射将这些类与接口/基类混合在一起?

3 个答案:

答案 0 :(得分:1)

为什么选择SomeTypeWrapper而不是SomeObjectWrapper?

public class SomeObjectWrapper : ISomeType
{
    Object _someObject;
    PropertyInfo _valuePropertyInfo;

    public SomeObjectWrapper(Object wrappedSomeObject)
    {
        _someObject = wrappedSomeObject;
        _valuePropertyInfo = _someObject.GetType().GetProperty("Value", System.Reflection.BindingFlags.Public);
    }

    public object Value
    {
        get { return _valuePropertyInfo.GetValue(_someObject, null); }
        set { _valuePropertyInfo.SetValue(_someObject, value, null); }
    }
}

答案 1 :(得分:1)

你可以试试温莎的Duck Typing Extensions。这意味着您需要注册每种类型。

container
    .Register(Component.For(typeof(SomeType1)).Duck<ISomeType>())
    .Register(Component.For(typeof(SomeType2)).Duck<ISomeType>());

如果名称相似,您可以使用linq和注册AllTypes语法来减少代码。

或者在短期内创建一个可以返回所需对象的工厂,为每种类型实现具体对象。不,您使用的界面可以在以后删除工厂,并将其替换为影响最小的其他内容:

public class SomeTypeWrapperFactory
{
    public ISomeType<int> CreateWrapper(SomeType1 someType1)
    {
        return new SomeType1Wrapper(someType1);
    }

    public ISomeType<string> CreateWrapper(SomeType2 someType2)
    {
        return new SomeType2Wrapper(someType2);
    }
}

public class SomeType1Wrapper : ISomeType<int> { ... }
public class SomeType2Wrapper : ISomeType<int> { ... }

无论您如何实现包装器,无论是单独使用还是使用类似神的类,您都可以更改包装的完成方式并保持其余代码的清洁。

答案 2 :(得分:0)

已编辑使用LinFu的.NET 3.5 您可以使用LinFu而不是Castle。然而,你无论如何都会使用反射,无论是使用Castle还是使用Linfu的DynamicProxy,只能隐藏在库的内容中而不是暴露在代码中。因此,如果您要求避免使用反射是出于性能问题,那么使用此解决方案就不会真正避免使用反射。 在那种情况下,我会亲自选择Orsol的解决方案。

然而:这是LinFu的一个例子。

public interface ISomeType {
    object Value{get; set;}
}

public class SomeType1
{
  public int Value { get; set; }
}

public class SomeType2
{
  public string Value { get; set; }
}

public class SomeTypeWrapperFactory
{

    public static ISomeType CreateSomeTypeWrapper(object aSomeType)
    {
        return aSomeType.CreateDuck<ISomeType>();
    }        
}


class Program
{
    public static void Main(string[] args)
    {
        var someTypes = new object[] {
            new SomeType1() {Value=1},
            new SomeType2() {Value="test"}
        };


        foreach(var o in someTypes)
        {
            Console.WriteLine(SomeTypeWrapperFactory.CreateSomeTypeWrapper(o).Value);
        }
        Console.ReadLine();
    }
}

由于你不知道SomeType的类型直到运行时,我不会使用mixins,而是访问者模式(我知道这不会回答关于如何使用mixins的问题,但我只是想到了我投入2美分。

使用动态的.NET 4 有关使用c#4的动态关键字,请参阅Bradley Grainger's post here以实现访问者模式。 在您的情况下,从您的SomeType字典中读取所有“值”属性可以这样工作:

public class SomeType1
{
  public int Value { get; set; }
}

public class SomeType2
{
  public string Value { get; set; }
}

public class SomeTypeVisitor
{
    public void VisitAll(object[] someTypes)
    {
        foreach(var o in someTypes) {
            // this should be in a try-catch block
            Console.WriteLine(((dynamic) o).Value);
        }

    }
}
class Program
{
    public static void Main(string[] args)
    {
        var someTypes = new object[] {
            new SomeType1() {Value=1},
            new SomeType2() {Value="test"}
        };

        var vis = new SomeTypeVisitor();

        vis.VisitAll(someTypes);            
    }
}