禁止访问公共列表的方法

时间:2017-10-13 08:46:47

标签: c# oop

这是一个极其简化的问题说明。鉴于我有这个:

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>方法,但我只需要为其中一些实现自定义实现,不是全部..或者还有其他方法吗?

3 个答案:

答案 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 ...
}