2013-09-01 117 views
1

我有一个List<T>类型的私人成员,其中T是丰富的域对象。我的应用程序中的域包含许多方法,期望T通过IList公开一个List <域对象><interface>

我想只公开IList<IT>类型的公共属性,其中T : ITIT由DTO实现的小占用空间。

由于我不能投List<T>IList<IT>我在财产声明中使用List<T>.ConvertAll

这是否有更好的方法来做到这一点?更快,更优雅?

编辑,以提供额外的细节

T是一个基类,为其存在多个派生类的,并且每个这些派生类的有多种不同的口味(在运行时加载的配置)。表示层中的用户可以添加/更改/删除任何配置的这些派生类的任何实例。这些实例也可以由用户定向链接,但是有一些复杂的规则可以管理哪些链接是允许的;一些在编译时已知,一些仅在运行时才知道(基于配置)。有些情况可能是双向链接的,有些是交叉链接的,有些只有一个方向是单一的,有些只有一个方向,有些则根本不存在。

为此,T包含任何此类链接的有效目标列表。表示层以图形方式突出显示这些有效目标,并且如果目标不在有效列表中,则不允许链接。当新创建,更改或删除任何实例时,需要重新评估每个实例的ValidTargets列表并可能更改。

工厂和服务类期望在其上运行的T类上有很多其他成员和方法。有些行为与上面的例子非常相似。这些都不应该暴露在组件之外。

+2

它需要是可变的吗? – SLaks

+0

Stefan也在@SLaks暗示。这取决于您期望客户实际使用该列表的内容。他们是否允许添加或删除它?它是由多个客户共享的吗?它本身是否代表一个领域概念(例如命令,查询,...)?等等。这取决于你的用例。 – Alex

+1

如果您的列表*为*只读,您可以使用IReadOnlyList 。 – zmbq

回答

0

假设你可以做这样的事情:

// NOT REAL CODE 
public interface IMyInterface { } 
public class MyRealClass : IMyInterface { } 

... 

public class Domain 
{ 
    private List<MyRealClass> myList; 

    public IList<IMyInterface> Interfaces { get { return (IList<IMyInterface>)this.myList; } } 
} 

什么是从这样停止这种Domain类的用户?

public class MyFakeClass : IMyInterface { } 

... 

domain.Interfaces.Add(new MyFakeClass()); 

显然,这将是问题,因为myList在现实中是一个List<MyRealClass>,但编译不能保证,只有MyRealClass实例添加到列表(它需要一个运行时检查)。换句话说,这种行为不是类型安全的,所以编译器不会允许它。

可以暴露出的IList/ICollectionIMyInterface —这是类型安全的,但不保证只有你MyRealClass被添加到列表中。

public class Domain 
{ 
    private List<IMyInterface> myList; 

    public IList<IMyInterface> Interfaces { get { return this.myList; } } 
} 

或者,也可以因为类型参数是协变(参见Covariance and Contravariance in Generics)暴露的IMyInterface一个IEnumerable(或IReadOnlyList,如zmbq建议)—。虽然这不会允许您将新项目添加到集合中。

public class Domain 
{ 
    private List<MyRealClass> myList; 

    public IEnumerable<IMyInterface> Interfaces { get { return this.myList; } } 
} 

另一个解决方案是实现实际实现的IList<IMyInterface>但如果用户试图将除MyRealClass任何其他抛出异常你自己的集合类。这不是一个特别优雅的解决方案,实际上,它与简单地公开IList<MyRealClass>没有什么不同。

+0

域中的内部工厂负责创建'MyRealClass',所以我不害怕接收'MyFakeClass'。真正的目标是隐藏内部IP并尽量减少DTO的大小。由于'MyRealClass'有很多方法,我想避免让每个DTO为这些创建一个存根,因为接口需要一个。 –

+0

标记为答案。尽管我从来没有找到更快或更优雅的方式来解决我通过DTO仅公开部分域的问题,但我认为你的回答解释了为什么你不能在两个不同类型的列表之间进行投射。 –

-1

如果我正确理解你,你应该可以使用Linqs select方法将你的列表转换为IList。所以你正在做这样的事情:

List<T> myList = ... 
IList<IT> b = myList.Select(a=> a as IT).ToList(); 
+0

这与'myList.ConvertAll(a => as IT)'是一样的。 'Select'是'ConvertAll'的懒惰版本。 – Teejay

相关问题