BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

C#的未来:扩展属性及更多

| 作者 Jonathan Allen 关注 530 他的粉丝 ,译者 邵思华 关注 3 他的粉丝 发布于 2015年5月4日. 估计阅读时间: 6 分钟 | Google、Facebook、Pinterest、阿里、腾讯 等顶尖技术团队的上百个可供参考的架构实例!

C#未来系列文章中的下一条提议希望能够使用扩展属性。这一功能是大家期盼已久的特性,但通常来说被认为是不可行的,因为它会带来内存泄露。

实际上,实现扩展属性所需的基础功能在.NET 4.0中就已经具备了。这里的秘密在于,你可以使用某个ConditionalWeakTable类以保存对象与扩展字段之间的映射。其实创建ConditionalWeakTable的目的正是为了提供这种动态类型语言的功能。

Sam Harwell为我们演示了如何正确地使用这个类:

private static readonly ConditionalWeakTable> _extensionField_f;
public static FieldType get_f(T obj)
{
    StrongBox box;
    if (!_extensionField_f.TryGetValue(obj, out box))
        return default(FieldType);
    return box.Value;
}
public static void set_f(T obj, FieldType value)
{
    StrongBox box = _extensionField_f.GetOrCreateValue(obj);
    box.Value = value;
}

扩展类

以上这种方式虽然能够实现最终的目的,但它的语法并不简洁。因此一种被称为“扩展类”的概念就能够派上用场了。可以通过以下语法定义一个扩展类:

public extension class MyClassExtensions : MyClass

在MyClassExtensions中定义的每个方法或属性都会自动成为MyClass中的扩展方法或属性。在MyClassExtensions中定义成员看起来与在MyClass中直接定义成员没有任何差别。这种方式让你能够避免使用旧的语法,即将某个方法定义为静态方法,并在参数中包含一个“this”关键字。

在扩展类中定义的字段和事件将通过上述的ConditionalWeakTable所实现。属性可以明确指定对应的字段或是自动实现,在后一种情况下,其内部实现依然依赖于ConditionalWeakTable的使用。

静态扩展

由于扩展类概念的存在,使得一种被称为“静态扩展方法”的使用方式也成为可能。它允许你为某个类添加一个静态方法,与你为对象添加成员方法的方式相同。

为了澄清这种用法,Erik Schierboom这样写道:

比方说,它能够允许我扩展xUnit中的Assert类。

思考

虽然这种做法在动态类型语言中很常见,但这种能够为某个类任意添加状态的能力还是让某些开发者感到有些不安。HellBrick总结了人们对于扩展字段的顾虑:

看上去对这种功能的尝试是很自然的事,但我担心这个特性可能会很容易遭到误用。如果你想要为某个类的实例附加一个额外的状态,我认为在大部分情况下,更好的方式是继承这个类,或是为它创建一个包装器。无论是哪种做法,你的意图都很明确。使用某种像魔法一样自动附加的弱字段引用,就不清楚它的生命周期,将这种方式作为类的设计方式或许是一种非常廉价的方案,并且具有一定吸引力。但同时,它可能会导致出现一些复杂的代码,增加日后维护的困难。

对上述说法的一种反对意见认为,如果没有扩展字段,那么也不可能通过扩展提供自动属性。Chrisaut继续这个讨论:

即使扩展字段这条提议通不过,只要属性的setter仍然是允许使用的,就会有开发者尝试着提出自己的解决方案,而其中有半数都存在着一些微秒的错误。比方说,他们可能会创建一个Dictonary<MyClass, string>并保存在属性中。额……这样一来这个实例就永远不会被垃圾回收了,这将带来内存泄露。大多数开发者甚至不知道弱引用和ConditionalWeakTable的存在。

因此如果我们真的要阻止附加额外状态的行为(也就是不希望引入扩展字段),那么我感觉唯一的方法就是明确地表示只允许属性的getter,不支持属性的setter,或者在自动属性中只支持getter。

这样一来大概就可以了。但我猜使用者会试图打破这一限制,我不确定只支持getter是否能够涵盖用户所需的所有场景。或者我们也可以说它已经涵盖了足够多的场景,而剩余的部分不值得实现。

扩展字段的另一个问题是对可回收对象的存储。如果你在ConditionalWeakTable中保存了一个可回收(disposable)对象,你需要找到一种方式,在父对象被垃圾回收时显式地回收该对象。ConditionalWeakTable不会自动为你实现这一功能,而依赖于终结器又存在着风险。

查看英文原文:C# Futures: Extension Properties and More

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我
社区评论

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT