2014-10-20 96 views
1

我想实现一个行为类似于Dictionary<string, dynamic>的类,因为动态关键字导致了由CAS问题导致的奇怪异常,我无法在项目的其他位置解析该异常。基本上我想要一个字典,允许获取和设置异构值,而无需显式投射。重新实现字典<string,dynamic>

我试图得到一个内部的Dictionary<string, Tuple<Type, object>>,目的是为了正确铸造它。我想超载方括号的操作员获取和设置。但是,我无法弄清楚这样做的正确语法。我想做这样的事情:

class DynDictionary<T> 
{ 
    private Dictionary<string, Tuple<Type, object>> _dict = new Dictionary<string, Tuple<Type, object>>(); 

    public T this[string key] 
    { 
    get { return (_dict[key].Item1)_dict[key].Item2; } 
    set { _dict[key] = new Tuple(typeof(value), value); } 
    } 
} 

当然,它不能编译的原因有几个。但是,我甚至无法使用泛型类来完成这项工作,而且我也不需要泛型类,因为在实例化时我必须指定类型,这正是我想要避免的。

有没有解决方案?我在干什么我不该碰的东西?

+3

你能表现出更多的周边代码来获得完整的图片? – 2014-10-20 13:52:02

+3

只是为了检查,你有没有考虑过ExpandoObject? – 2014-10-20 13:59:47

+0

@manuFS:我没有,但我有清楚的感觉,我会遇到与'dynamic'一样的问题。例如,我看到ExpandoObject实现了IDynamicMetaObjectProvider,它是。只有NET 4,所以很可能会有同样的问题。 – user1846231 2014-10-20 14:14:16

回答

2

不,没有这样的选择。

正如你提到的,对于:

public T this[] { get; set; } 

你需要一些“通用性”,在外部范围,为this[]也无法对自己的通用。所以,你的课/不管是generic-<T>,并强制用户不仅要指定T,而且要为所有元素指定一个T。

对于Dictionary<string, Tuple<Type, object>>,你可以有最好的是:

public object this[] { get; set; } 
public IMyInterface this[] { get; set; } 

这是因为在编译的时候,你的整个字典类没有对项目类型的任何信息。该代码限于object,如Typle<Type,object>。你唯一能做的就是返回一个object,或者尝试将它转换成其他已知类型,比如接口。

如果从使用this[]辞职,你可以尝试做一个“聪明的getter”:

public TValue GetItem<TValue>(TKey index) { ... } 
public void SetItem<TValue>(TKey index, TValue value) { ... } 

这将只是做所有的铸件(return (TValue)tuple.Item2等)。但是,当“TValue”未知时,在上下文中使用时会出现一些问题,例如使用困难。所以,你也可能需要

public object GetItem(TKey index) { ... } 
public void SetItem(TKey index, object value) { ... } 

只是为了避免繁琐的GetItem<object>。当然,无论使用哪种TValue版本,用户都需要明确地指定TValue(除了SetValue可能推断的地方,不一定正确)。

现在,让我们回到基础。你有什么想法

public T this[] { get; set; } 

anyways,hm?当你将Tuple中的Type和Object捆绑在一起时,你似乎希望能够在Dictionary中包装一个异源项目,对吗?那么,告诉我,如何最终用户和/或代码/编译器将如何能够猜到正在返回wnat:

var foo = myDictionary["bar"]; 

这将是foo变量的类型?编译器无法猜测,因为在编译时myDictionary只知道它会保存一些Tuple<Type,object>,但它现在不包含任何东西。此外,它实际上还没有存在,因为它正在被编译。根据你的Tuple的确切类型强制“返回值”是不可能的。

此外,为什么你甚至会在该元组中携带Type?保存在字典中的object总是知道它的类型。你不需要携带类型,你可以简单地在该对象上使用.GetType(),但效果相同。如果你想保留上传的类型信息(例如:有一个Bar从Foo继承下来,upcast Bar作为Foo并放入magiccontainer中,然后作为Foo not Bar取回),那么你的运气有点不足。没有帮助,并会返回你“动态对象”,这将知道自己是“酒吧”,并允许您动态调用所有酒吧方法。我认为携带这种类型是完全没有必要的,除非你有一些强大的屏蔽/隔离要求 - 但在这种情况下,简单的“携带类型并铸造”根本不会帮助。对于这样的事情,你可能需要动态代理。由于我已经有了健谈,最后(有点没用,但你可能还是想听到它)注意:实际上可以强制一个“强制类型”转换为存储在“类型”中的类型。你可以用表达式来做到这一点。您可以动态构建Expression<>,将其与正确的Type对象绑定并调用。从https://stackoverflow.com/a/3983342/717732

借款代码它看起来像:

public static ???? castAndReturn(object item, Type type) 
{ 
    var p = Expression.Parameter(typeof(object), "i"); 
    var c = Expression.Convert(p, type); 
    var ex = Expression.Lambda<Func<object, ????>>(c, p).Compile(); 

    return ex(item); 
} 

但同样,有什么返回类型你投入????占位符,嗯?唯一的可能性是object,dynamic,或者定制的众所周知的通用基础类型。回到原点。 D'哦。抱歉。没有,真的,方式。

+0

我很喜欢你的最后一点。我想它应该猜测与Dictionary 猜测的相同的东西,不管它是什么。 – user1846231 2014-10-20 14:40:15

+0

@ user1846231:使用'Dictionary '它猜测它是....'dynamic'。说真的,不要开玩笑。语法'dynamic foo = 5; foo =新妈妈(); foo = 123.0'是正确的。使用.Net4,我们得到了一个新的名为“dynamic”的神奇“数据类型”,您几乎可以在任何可以使用像int或Mom这样的普通数据类型的地方使用它。“Dictionary '不是魔法。它只是返回它的元素类型,而元素类型是'dynamic'。 – quetzalcoatl 2014-10-20 14:42:43

+0

我还添加了一个不是关于“真正的动态投射”,如果你想玩表情..但尽管可能和有趣的是,它在这个目前的问题中没有任何收获..呃,除非你想要沉重lambdaize /表达你的所有代码。不是真的值得恕我直言。 '获得(“酒吧”)'将更加可用,可读和容易。 – quetzalcoatl 2014-10-20 14:45:20

0

这听起来像你正试图实现一个属性容器暴露一个通用的索引器,这是(你知道)在C#中不可能的。但是,可以按照IEditorOptions界面中看到的模式实施类似的策略。特别要注意以下几个特点:

  1. 与键关联的强类型由创建EditorOptionKey<T>泛型类型,而不是使用只是一个字符串表示。
  2. GetOptionValue<T>SetOptionValue<T>方法会替换您当前使用的索引器属性。这些方法的泛型类型参数是从作为参数传递的EditorOptionKey<T>推断出来的。

使用这个类可能看起来像以下(从DefaultOptions类使用多个字段)代码:

IEditorOptions options = ...; 
bool replicate = options.GetOptionValue(DefaultOptions.ReplicateNewLineCharacterOptionId); 
options.SetOptionValue(DefaultOptions.IndentSizeOptionId, 2); 
options.SetOptionValue(DefaultOptions.ConvertTabsToSpacesOptionId, true); 
相关问题