我有一个第三方设计糟糕的图书馆,我必须使用它
它有各种类型,我们称之为 SomeType1 , SomeType2 等。
这些类型中没有一个共享一个公共基类,但都有一个名为Value的属性,具有不同的返回类型
我想要做的就是能够在这个类中使用Mixin,这样我就可以调用someType1Instance.Value
和someType2Instance.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)
我怎样才能尽可能少地使用反射将这些类与接口/基类混合在一起?
答案 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);
}
}