I have a dependency graph class built in c# with several private data properties that are supposed to only be accessed, but more importantly modified, through the standard 'getter / setter' methods. I came across a test which exposes the public interface through casting an IEnumerable as an ICollection. I am unsure how to prevent this obviously unwanted action.
Here is the failing unit test that confirms I am exposing private data:
[TestMethod]
public void PrivateDataTest()
{
try
{
DependencyGraph dg = new DependencyGraph();
dg.AddDependency("a", "b");
dg.AddDependency("a", "c");
ICollection<string> temp = (ICollection<string>)dg.GetDependents("a");
temp.Add("d");
Assert.IsTrue(new HashSet<string> { "b", "c", "d" }.SetEquals(temp));
Assert.IsTrue(new HashSet<string> { "b", "c" }.SetEquals(dg.GetDependents("a")));
}
catch (Exception e)
{
if (!(e is NotSupportedException || e is InvalidCastException))
Assert.Fail();
}
}
And here is my method 'GetDependents' which is called simultaneously to the ICollection cast.
/// <summary>
/// Enumerates dependents(s).
/// </summary>
public IEnumerable<string> GetDependents(string s)
{
try
{
return this.data[s].getDependencies();
}
catch (Exception e)
{
return new List<string>();
}
}
Additionally this is the 'data' property being accessed:
// key = node name, value = node object and all node data e.g. dependencies, dependees, name
private SortedDictionary<String, Node> data;
The 'data' property contains a string which represents the node's name e.g. "a", and a custom Node object. The name is the key to the actual Node object, which is a nested class that is used to store a node's name, and list of dependencies / dependees. Here is my my Node class (Note. I stripped away a lot of methods that are irrelevent for the purpose of this question):
public class Node
{
private String name;
private List<String> dependencies;
private List<String> dependees;
/// <summary>
/// construct a new Node by passing in a name and lists for both the dependencies and the dependees
/// </summary>
/// <param name="_name"></param>
/// <param name="_dependencies"></param>
/// <param name="_dependees"></param>
public Node(String _name, List<String> _dependencies, List<String> _dependees)
{
name = _name;
dependencies = _dependencies;
dependees = _dependees;
}
/// <summary>
/// construct a Node with just a name and blank dependent lists
/// </summary>
/// <param name="_name"></param>
public Node(String _name)
{
name = _name;
dependencies = new List<string>();
dependees = new List<string>();
}
/// <summary>
/// getter for the dependencies property
/// </summary>
public List<String> getDependencies()
{
return this.dependencies;
}
}
I hope this is enough information for someone to understand the scope of my question as it relates to my graph. I will reiterate; how would one go about preventing this breach of interface via casting? This is my first question on StackOverflow, and I'm also fairly new to the C# language, so I apologize if my formatting / lack of understanding is incorrigible.