隐藏接口实现 及 ReadOnlyDictionary

隐藏接口实现 及 ReadOnlyDictionary

摘要:本文介绍了如何从类型中隐藏掉接口的某个成员,并介绍了应用这种技巧实现的只读字典——ReadOnlyDictionary。

接口代表着一种契约。但有的时候,接口所达成的契约并不适用于全部的场景,或者说,接口可能定义得“太宽了”。这个时候,就有必要隐藏起某些接口成员。

然而,接口既然是一种“契约”,这就要求实现方必须为接口中的所有成员提供实现。所以,这里说到的“隐藏”,是指从对象的视角上隐藏。换言之,就是只有直接在对象上调用成员时,看不到某些接口成员,但如果将对象强制转换为接口类型,依然能看到所有的接口成员。

在C#中,接口的显式实现可以帮助我们实现这一功能。下面的代码可以帮助我们回忆一下接口的显式实现。

public interface IFoo
{
    void Foo();
}

public class Implement : IFoo
{
    // 显式实现的接口成员:
    void IFoo.Foo() { /* 实现 */ }
}

public class Program
{
    static void Main()
    {
        Implement impl = new Implement();
        impl.Foo();  // 编译错误。显式实现的接口成员不能直接在对象上调用。

        IFoo foo = (IFoo)impl;
        foo.Foo();  // 编译正确。
    }
}

下面举个实际的例子。熟悉集合的朋友一定对ICollection<T>接口不陌生。该接口定义了集合类型的一般成员,包括Add、Clear、Contains、CopyTo、GetEnumerator、Remove、Count等等。从集合的角度来看,这些成员是很充分的。

但是,考虑这样一个场景——希望实现一个只读集合类,这个类不允许改变集合的内容。很明显,只读集合也“是一个”集合,所以理应实现ICollection<T>接口。然而,接口中的Add、Clear和Remove等方法无疑是破坏了集合的“只读”特征。

因此,在实现只读集合时,可以用这样一种方式来实现只读集合类——在类型中定义一个普通集合类型的私有字段,也就是说,让只读集合“包装”一个普通集合。这个只读集合类依然实现ICollection<T>接口,其中不会破坏“只读”性质的成员,直接采用普通集合的实现;而对于会破坏“只读”性质的接口成员,则采用显式接口实现的方式,从对象实例中隐藏掉该成员。

最后,为了防止用户将对象强制转换为接口类型,并试图调用那些会破坏只读性质的成员,需要在这些成员的实现代码中抛出异常(推荐使用NotSupportedException异常)。

例如,下面介绍一个ReadOnlyCollection类:

首先,令该类实现ICollection<T>接口,定义一个私有字段存放待包装的普通集合,并在构造器中为其赋值。

public class ReadOnlyCollection<T> : ICollection<T>
{
    private ICollection<T> _collection;

    public ReadOnlyCollection<ICollection<T> collection)
    {
        if(collection == null)
            throw new NotSupportedException();

        _collection = collection;
    }
}

对于那些不会破坏只读性质的成员,直接利用普通集合的实现即可。这里以Contains方法为例。

public bool Contains(T value)
{
    return _collection.Contains(value);
}

而对于那些会破坏只读性质的成员,则采用接口成员的显式实现,并在实现代码中抛出异常。这里以Add方法为例。

void ICollection<T>.Add(T value)
{
    throw new NotSupportedException();
}

非常cool的是,.NET从2.0开始,已经为我们提供了这样一个只读集合,位于System.Collections.ObjectModel命名空间中。

当然,这里为了能够简单地说明问题,提供的代码示例与.NET中的实现并不太一样,有兴趣的朋友可以用.NET Reflector查看ReadOnlyCollection<T>类的源代码。

遗憾的是,.NET只为我们提供了只读集合,却没有提供只读字典(或者是提供了,但我不知道)。所以在这里我仿照ReadOnlyCollection<T>类,编写了一个ReadOnlyDictionary<TKey, TValue>类。代码略长,这里就不贴了,感兴趣的朋友可以通过下面的链接下载:

http://files.cnblogs.com/AndersLiu/ReadOnlyDirectionary.zip

posted @ 2009-04-16 12:57 Anders Liu 阅读(2061) 评论(13) 编辑 收藏

 回复 引用 查看   
#1楼 2009-04-16 13:11 winzheng      
GOOD,又学一招,3x
 回复 引用 查看   
#2楼 2009-04-16 13:55 Jeffrey Zhao      
其实WPF的类库里有ReadOnlyDictionary,但是基础类库里没有……
// 文件名错了,呵呵

 回复 引用 查看   
#3楼 2009-04-16 13:57 一人行      
不就是典型的GOF23之适配器模式吗?
 回复 引用 查看   
#4楼 2009-04-16 13:57 Jeffrey Zhao      
还有提个意见,现在ReadOnlyCollection的实现是传入一个外部Collection,但是外部Collection的修改会造成ReadOnlyCollection内容的改变。
WPF中的ReadOnlyDictionary的构造函数会接受一个布尔值,表示会不会把当前元素拷贝一遍,这样就修改不了了。

 回复 引用 查看   
#5楼 2009-04-16 14:16 飞林沙      
ReadOnlyCollectionBase
用这个呢?

 回复 引用   
#6楼 2009-04-16 14:43 gfdgfdhg[未注册用户]
搬家公司
 回复 引用 查看   
#7楼 2009-04-16 15:17 韦恩卑鄙      
--引用--------------------------------------------------
Jeffrey Zhao: 还有提个意见,现在ReadOnlyCollection的实现是传入一个外部Collection,但是外部Collection的修改会造成ReadOnlyCollection内容的改变。
WPF中的ReadOnlyDictionary的构造函数会接受一个布尔值,表示会不会把当前元素拷贝一遍,这样就修改不了了。
--------------------------------------------------------
所以说在传入一个外部Collection前 注意做一次复制就对了吧




老赵 我昨天手痒写了一个前缀树 还真是慢的要死阿


public class PerfixCollection<TNode> : ICollection<IEnumerable<TNode>> where TNode : IComparable<TNode>, IComparable, IEquatable<TNode>


 回复 引用 查看   
#8楼 2009-04-16 15:23 韦恩卑鄙      
懒人来献宝了

最快速的隐藏接口实现操作

1 建立一个类
2 声明要实现的接口
3 在 工程中建立一个类图 把这个类拖进去
4 类图上会显示每个接口的名字(头上) 比如声明了了Idictionry 就会有 Ienumxxanle,Icollection等等若干继承的接口。

对以上接口 别右key
(隐藏实现)
(显示实现)


oh yeah



 回复 引用 查看   
#9楼 2009-04-16 16:52 Anders06      

 回复 引用 查看   
#10楼 2009-04-16 18:27 Ivony...      
显示接口实现莫非就是用来隐藏接口成员的?
 回复 引用 查看   
#11楼 2009-04-17 14:05 Microshaoft      
http://www.cnblogs.com/Microshaoft/archive/2005/03/28/127527.html

在“重构”中,有此技巧
《Refactoring: Improving the Design of Existing Code》
3.21 Refused Bequest: Replace Inheritance with Delegation
如果不想修改superclass,还可以运用 Replace Inheritance with Delegation 来达到目的。
也就是以委托取代继承,在 subclass 中新建一个 Field 来保存 superclass 对象,
去除 subclass 对 superclass 的继承关系,委托或调用 superclass 的方法来完成目的。
这里的委托不是 .Net 的 Delegation !
仅仅就是代理人、替代品、代表的意思!

 回复 引用 查看   
#12楼[楼主] 2009-04-17 15:53 Anders Liu      
@Microshaoft

这里就是用的这种方式啊,那个私有字段不是保存了一个所谓的superclass的实例么?

只不过是,你这个“代理人”,也得实现和“被代理人”一样的接口吧?要不怎么做到可以真正替代之?

 回复 引用 查看   
#13楼 2011-11-24 09:44 aguicn      
加internal,我是这么做的,很好用的
internal WebDictionary(IDictionary<TKey, TValue> innerDirectionary)
{
if (_dictionary == null)
throw new ArgumentNullException();
_dictionary = innerDirectionary;
}

internal WebDictionary()
{
_dictionary = new Dictionary<TKey, TValue>();
}

发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1437125 4G57S62V6GI=