0

我有一个我必须使用的设计糟糕的第 3 方库。
它有各种各样的类型,我们称它们为SomeType1SomeType2
。这些类型都不共享一个公共基类,但都有一个名为 Value 的属性,具有不同的返回类型。
我想要做的就是能够 Mixin 这个类,这样我就可以调用someType1Instance.Value并且someType2Instance.Value不关心它是什么 concreate 类型,也不关心返回类型是什么(我可以使用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)

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

4

3 回答 3

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); }
    }
}
于 2010-11-02T15:56:07.367 回答
1

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

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> { ... }

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

于 2010-11-02T16:18:36.287 回答
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 使用动态 请参阅Bradley Grainger 在此处使用 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);            
    }
}
于 2010-11-04T16:03:49.593 回答