2010-05-03 62 views
1

我在写一个接口,它有一个我只想读取的集合属性。我不希望界面的用户能够修改集合。我发现创建一个只读集合属性的典型建议是属性的类型设置为IEnumerable的是这样的:正在使用YIELD以只读方式返回集合?

private List<string> _mylist; 
public IEnumerable<string> MyList 
{ 
get 
    { 
     return this._mylist; 
    } 
} 

然而,这并不妨碍用户从铸造了IEnumerable回到列表和修改它。

如果我使用Yield关键字而不是直接返回_mylist,这将阻止我的界面的用户能够修改集合。我认为是这样,因为我只是一个接一个地返回对象,而不是实际的集合。

private List<string> _mylist; 
public IEnumerable<string> MyList 
{ 
get 
    { 
     foreach(string str in this._mylist) 
     { 
      yield return str; 
     } 
    } 
} 

回答

0

是的,结果序列将是只读的。它还具有延迟执行的优点,因此只有在某些客户端代码枚举了序列中的项目之后才会实际评估循环(尽管如果在客户端代码试图枚举内部集合之前更改内部集合,这可能会产生意想不到的后果) 。

+1

在这里,延期执行只不过是开销。 – SLaks 2010-05-03 23:27:00

+1

有时我*强制*评估只是为了避免延误的副作用(ick !!!) – 2010-05-03 23:28:59

4

是的,它是不可变的。

但是,您应该改用ReadOnlyCollection<string>

例如:

MyClassName() { //(Constructor) 
    MyList = new ReadOnlyCollection<string>(_myList); 
} 

private List<string> _mylist; 
public ReadOnlyCollection<string> MyList { get; private set; } 
+0

是的,但请注意,“不变性”的一部分来自包含的对象,在这种情况下'字符串'也是不可变的。一般来说,调用者不能更改集合的成员资格,但可以更改内容(对于ref类型)。有时候这是可取的,有时候不是。 – 2010-05-03 23:32:18

+1

@Ben:对于ref类型的任何集合都是如此,除非你克隆每一个元素,否则一般包括IEnumerable ... – 2010-05-03 23:33:38

+0

我的情况集合包含不可变对象(一个带有私有集合访问器的结构),所以这不是问题。 – 2010-05-03 23:46:04

3

没错,就是工作。这不是唯一的方法。下面是工作在.NET 3.5的另一种方式:

public IEnumerable<string> MyList 
{ 
    get { return _myList.Skip(0); } 
} 

从这个question这也显示了一些其他的方法可以做到这两者。

(使用Google找到)。

5

这是一种合理的方式来阻止您的班级的用户回到列表并访问成员。另一种选择是使用_mylist。 AsReadOnly(),它返回ReadOnlyCollection<T>

但是,我建议不要担心用户如何通过投射到其他内部类型来“破坏”您的API。如果您将公共API设置为IEnumerable<T>,则这是最终用户将(或应该)使用的内容 - 而不是尝试投射到List<T>。试图阻止用户做一些不恰当的事情,并且像这样打破你的API是有问题的,特别是因为你无法防止所有不合适的用法。

+0

使用部分信任沙箱(在.NET或Java中)时,您的最后一句不再是真的。 – 2010-05-03 23:30:37

+0

@本:真的。但总的来说,我发现人们在没有理由这样做的情况下试图阻止投射。但是,在部分信任沙箱中,您可能不会直接提供访问权限,无论如何您都不必担心这一点。 – 2010-05-03 23:34:33

1

如果使用你的类的代码具有完全的信任,它总是可以反射和访问私人领域。这将是相当不礼貌的,但铸造返回值只会稍好一些。如果用户投掷你的财产并腐败国家,这是他们自己的错。

直接返回列表实例作为IEnumerable的一个好处是,LINQ可以优化像ElementAt()或Count()这样的方法的访问,而不会违反IEnumerable的基本约定(因为两者都可以通过枚举来完成)。