问题
I have a private member of type List<T>
where the T
are rich domain objects. The domain in my application contains many methods that expect a T
.
I want to expose only a public property of type IList<IT>
where T : IT
. IT
has a small footprint which is implemented by a DTO.
Since I cannot cast a List<T>
to IList<IT>
I am resorting to using List<T>.ConvertAll
in the property declaration.
Is this there a better way to do this? Faster, more elegant?
Edit to provide additional detail
The T
are a base class, for which a number of derived classes exist, and each of these derived classes comes in many different flavors (configurations loaded in runtime). The user in the presentation layer can add/change/remove any instances of these derived classes of any configuration. The instances can also be linked directionally to each other by the user, but there are some complex rules that govern what links are allowed; some known at compile time, some known only at runtime (based on the configurations). Some instances may be double-linked, some cross-linked, some only single in either direction, some in only one direction, and some not at all.
For this purpose the T
contains a list of the valid targets for any such links. The presentation layer graphically highlights these valid targets and does not allow linking if the targets are not in the valid list. When any instance is newly created, changed or removed the ValidTargets list of each instance needs to be re-evaluated and may change.
There is a lot of other members and methods on the T
class that the factory and services classes expect to operate on. Some behave very similar to the example above. None of these should be exposed outside of the assembly.
回答1:
Suppose you could do something like this:
// 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; } }
}
What's to stop a user of this Domain
class from doing this?
public class MyFakeClass : IMyInterface { }
...
domain.Interfaces.Add(new MyFakeClass());
Obviously this would be problematic because myList
is in reality a List<MyRealClass>
, but the compiler cannot ensure that only instances of MyRealClass
are added to the list (it would require a run-time check). In other words, this kind of behavior is not type-safe, so the compiler won't allow it.
You can expose a IList
/ ICollection
of IMyInterface
—which is type-safe, but doesn't ensure that only your MyRealClass
is added to the list.
public class Domain
{
private List<IMyInterface> myList;
public IList<IMyInterface> Interfaces { get { return this.myList; } }
}
Alternatively, you can expose an IEnumerable of IMyInterface
(or IReadOnlyList, as zmbq suggests)—since the type parameter is covariant (see Covariance and Contravariance in Generics). Although this would not allow you add new items to the collection.
public class Domain
{
private List<MyRealClass> myList;
public IEnumerable<IMyInterface> Interfaces { get { return this.myList; } }
}
Another solution would be to implement your own collection class that actually implements of IList<IMyInterface>
but throws an exception if a user tries to insert anything other than a MyRealClass
. This isn't a particularly elegant solution and in practical terms, it's not any different from simply exposing an IList<MyRealClass>
.
回答2:
If I am understanding you correctly, you should be able to use Linqs select method to transform your list to IList. So you are doing something like:
List<T> myList = ...
IList<IT> b = myList.Select(a=> a as IT).ToList();
来源:https://stackoverflow.com/questions/18560845/exposing-a-listdomain-objects-via-an-ilistinterface