【问题标题】:No base class problem, How to use Castle.DynamicProxy Mixin in this particular case?没有基类问题,在这种特殊情况下如何使用 Castle.DynamicProxy Mixin?
【发布时间】:2011-05-04 00:32:19
【问题描述】:

我必须使用一个设计不佳的第 3 方库。
它适用于各种类型,我们称它们为 SomeType1SomeType2 等。
这些类型都不共享一个公共基类,但都有一个名为 Value 的属性,具有不同的返回类型。
我想要做的就是能够混入这个类,这样我就可以调用someType1Instance.ValuesomeType2Instance.Value,而不用关心它是什么concreate 类型,也不关心返回类型是什么(我可以使用@987654323 @)。
所以我的代码目前是:

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)

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

【问题讨论】:

  • 我们在谈论多少种类型?什么是第三方 api?
  • 13-14,微软动态CRM。

标签: c# .net reflection castle-dynamicproxy


【解决方案1】:

您可以尝试使用 Duck Typing Extensions 获取 Windsor。这意味着您需要注册每种类型。

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

如果名称相似,您可能可以使用 linq 和 register 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> { ... }

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

【讨论】:

  • "DeftTech 生成的代理显然不能被 DynamicProxy 代理...所以不要尝试为这些组件定义任何拦截器!同样,最佳解决方案是实现适当的鸭子-用 DynamicProxy 打字”。这是个问题,我打算拦截方法调用。
  • 尝试了工厂方法,这不好,因为直到运行时我得到我的字典时我才知道我的对象的类型。
【解决方案2】:

为什么是 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); }
    }
}

【讨论】:

  • 这是一种有效的方法,但如果可能的话,我真的不想使用反射。如果可能的话,我更愿意混入一个通用接口。
【解决方案3】:

编辑 .NET 3.5 使用 LinFu 您可以使用 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 中使用动态 请参阅Bradley Grainger's post here 使用 c#4 的动态关键字来实现访问者模式。 在您的情况下,从您的 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);            
    }
}

【讨论】:

  • 我不能使用 C# 4.0。我们的平台是 3.5。
  • 添加了使用 LinFu 的 DuckTyping 的解决方案
猜你喜欢
  • 2020-02-20
  • 1970-01-01
  • 1970-01-01
  • 2021-10-30
  • 2012-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多