这是一个极其简化的问题说明。鉴于我有这个:
interface IMyClass {
IList<int> MyList { get; set; }
void AddToList(int newVal);
}
public class MyClass: IMyClass {
public IList<int> MyList { get; set; }
public void AddToList(int newVal)
{
// custom implementation goes here
MyList.Add(newVal);
}
}
我得到了一个接口IMyClass
,我无法改变。任务是实现此接口以及addToList
方法。
问题在于即使我实施addToList
方法 - 用户仍有可能通过直接访问MyList
属性来添加项目!
有没有办法禁止用户直接在列表中使用Add()
,Remove()
,Insert()
?
到目前为止,我已尝试实现自定义MyList<T>
类,但问题是我必须实现所有可能的IList<T>
方法,但我只需要为其中一些实现自定义实现,不是全部..或者还有其他方法吗?
答案 0 :(得分:2)
由于您说您无法更改界面,因此您必须使用公开的属性IList<int>
。即使您显式实现接口(隐藏公共属性),也不会阻止用户将实例强制转换为接口类型以访问公共IList<int>
属性。
但是由于ReadOnlyCollection<T>
type实现了IList<T>
接口,您可以使用它来创建私有列表周围的只读包装器,因此您只能通过公共属性公开该只读包装器,但请将您的实际列表保密,以防止用户更改它。例如:
interface IMyClass {
IList<int> MyList { get; set; }
void AddToList(int newVal);
}
public class MyClass: IMyClass {
private readonly List<int> myPrivateList;
public IList<int> MyList { get; set; }
public MyClass()
{
// Create private list used internally
myPrivateList = new List<int>();
// Create read-only wrapper around myPrivateList used for the public MyList property
MyList = new ReadOnlyCollection<int>(myPrivateList);
}
public void AddToList(int newVal)
{
// Use myPrivateList instead of MyList
myPrivateList.Add(newVal);
}
}
虽然这对其他开发人员来说可能有点误导,但恕我直言,这样的解决方案应该在实施中正确记录。
答案 1 :(得分:1)
您可以将列表保密,不要将其公开给消费者。这样,消费者无法修改您的列表。
此外,通过使用IReadOnlyList,消费者甚至没有添加或删除。
private List<int> myList;
public IReadOnlyList<int> MyList => myList.ToArray();
但是,每次转换为Array都会导致性能下降。如果您不想浪费资源,并且绝对无法更改界面,则需要创建自己的IList实现:
public class MyClass: IMyClass {
private IList<int> myInnerList;
public IList<int> MyList { get; }
MyClass()
{
myInnerList = new List<int>();
MyList = new ProtectedList(myInnerList);
}
public void AddToList(int newVal)
{
myInnerList.Add(newVal);
}
}
/// <summary>
/// Encapsulates an IList in a way, so that you can only change it
/// through the original IList reference.
/// </summary>
class ProtectedList<T> : IList<T>, IReadOnlyList<T>
{
private IList<T> innerList;
public ProtectedList(IList<T> innerList) => this.innerList = innerList;
public T this[int index]
{
get => innerList[index];
set => throw new InvalidOperationException();
}
public int Count => innerList.Count;
public bool IsReadOnly => true;
public void Add(T item) => throw new InvalidOperationException();
public void Clear() => throw new InvalidOperationException();
public bool Contains(T item) => innerList.Contains(item);
public void CopyTo(T[] array, int arrayIndex) => innerList.CopyTo(array, arrayIndex);
public IEnumerator<T> GetEnumerator() => innerList.GetEnumerator();
public int IndexOf(T item) => innerList.IndexOf(item);
public void Insert(int index, T item) => throw new InvalidOperationException();
public bool Remove(T item) => throw new InvalidOperationException();
public void RemoveAt(int index) => throw new InvalidOperationException();
IEnumerator IEnumerable.GetEnumerator() => innerList.GetEnumerator();
}
}
答案 2 :(得分:0)
你可以在IList<T>
周围创建一个实现IList<T>
接口的包装器。这个包装器会将所有功能委托给它的内部IList<T>
,并且只是禁止直接操作该列表。类似的东西:
public MyList<T> : IList<T>
{
private IList<T> innerList;
public MyList(IList<T> innerList) { this.innerList = innerList; }
// valid operations are just delegated to the inner list...
public int Count { get { return innerList.Count; } }
// those you want to prohibit either do nothing or throw - depends on your desired outcome
public void Add(T item) { return; }
// and so on ...
}