Overriding the .Add method in a List of... with validation - c#

I have a Use Case where I want to validate a specific property in a list of objects to make sure it is unique. The basic setting can be seen in the code below.
class Program
{
static void Main(string[] args)
{
Directory myDirectory = new Directory("Interaction Design");
myDirectory.Books.Add(new Book("978-0-262-64037-4", "The Design of Everyday Things")); //Should be added
myDirectory.Books.Add(new Book("978-0-262-13474-3", "Designing Interactions")); //Should be added
myDirectory.Books.Add(new Book("978-0-262-13474-3", "Whoops, I slipped up")); //Should NOT be added
}
}
public class Directory
{
public Directory(string name)
{
Name = name;
}
public string Name { get; set; }
public List<Book> Books { get; set; } = new List<Book>();
}
public class Book
{
public Book(string isbn, string title)
{
Isbn = isbn;
Title = title;
}
public string Title { get; set; }
public string Isbn { get; set; }
}
Now, in the code above, adding a new Book in the List of Books should throw an exception if the ISBN number isn't unique.
I would like to extend on the .Add() method of the List and add that validation, but I'm not sure how to actually do that.
I've seen similar things, but they all assume that the Directory inherits from List and you write an overriding .Add method to the Directory - which doesn't look like a valid solution in this case.
Perhaps my general approach is backwards?
Please advice.

If you want uniqueness, use a collection that gives you that: a set.
Make an IEqualityComparer<book> that considers two books to be equal if the Isbns match and use it in a HashSet<Book> that represents your unique list of books:
public class Directory
{
public HashSet<Book> Books { get; }
= new HashSet<Book>(new BookEqualityComparer());
//...
private class BookEqualityComparer : IEqualityComparer<Book>
{
public bool Equals(Book x, Book y)
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null) ||
ReferenceEquals(y, null))
return false;
return x.Isbn == y.Isbn;
}
public int GetHashCode(Book obj)
=> obj.Isbn.GetHashCode();
}
}
And you are done, you can't have any duplicate books in Books.

One option is to create a new class and inherit from Collection<T> instead of List<T>, where you could overwrite InsertItem
e.g.
public class MyList: Collection<string>
{
protected override void InsertItem(int index, string newItem)
{
DoValidation();
}
}
called with the following call:
var myList = new MyList();
myList.Add("item1");

Why not this?
public class Directory
{
private List<Book> _books;
public IEnumerable<Book> Books
{
{
return _books;
}
}
Public void AddBook(Book book)
{
//Validate
if(valid)
{
_books.Add(book);
}
}
//Etc
}

Related

C# SOLID - Extend class and override/hide members without violating Open/Closed?

I need to extend a class where the child class will not use some members of the parent class. How should the unused members be handled and still respect the open/closed SOLID principle? I'm considering doing something like the code below, but existing code that uses reflection will get exceptions. If I put data in the deprecated members, existing code will produce unexpected results.
I feel that a totally different approach should probably be used. How to handle this situation without modifying existing code (which would also violate the open/closed principle)?
class Parent
{
public virtual int myVarMin { get; set; } = 0;
public virtual int myVarMax { get; set; } = 10;
public int myVar { get; set; }
public int unchanged1 {get; set;}
//.
//numerous other members that are irrelevant to the question
//.
public void doSomething(){/*do something*/}
//
}
class Child:Parent
{
//considered impementation from original question
public override int myVarMin => throw new NotSupportedException();
public override int myVarMax => throw new NotSupportedException();
public List<int> myVarList = new List<int>();
}
class MyExistingCode
{
public void Display(Parent parent)
{
foreach (var info in parent.GetType().GetProperties())
{
Console.WriteLine($"{info.Name}: {info.GetValue(parent)}");
}
}
}
Use the Obsolete attribute to inform the developers that your method is deprecated and they should use the new version.
[Obsolete("Method is deprecated, use Method2 please.")]
public void Method()
{
…
}
Here I've changed your code, so using reflection you can detect whether a method/property is deprecated or not, and it'll not throw an exception anymore.
public class Parent
{
public virtual int myVarMin { get; set; } = 0;
public virtual int myVarMax { get; set; } = 10;
public int myVar { get; set; }
}
public class Child : Parent
{
[Obsolete("Use other property")]
public override int myVarMin => throw new NotSupportedException();
[Obsolete("Use other property")]
public override int myVarMax => throw new NotSupportedException();
public List<int> myVarList = new List<int>();
}
class MyExistingCode
{
public void Display(Parent parent)
{
foreach (var info in parent.GetType().GetProperties())
{
var customeAttributes = (ObsoleteAttribute[])info.GetCustomAttributes(typeof(ObsoleteAttribute), false);
if (customeAttributes.Length > 0)
{
Console.WriteLine($"{info.Name} is deprecated.");
}
else
{
Console.WriteLine($"{info.Name}: {info.GetValue(parent)}");
}
}
}
}
I ended up doing something like this (the Parent and MyExistingCode classes were unchanged, so they comply with the open/closed principle):
class Child : Parent
{
public new int? myVarMin => null;
public new int? myVarMax => null;
public List<int> myVarList = new List<int>();
}
class MyNewCode : MyExistingCode
{
public new void Display(Parent parent)
{
foreach (var info in parent.GetType().GetProperties())
{
Console.WriteLine($"{info.Name}: {info.GetValue(parent) ?? "NULL"}");
}
}
}
I was surprised that I could hide an int with an int? without an error.
I will Accept another answer if it is better.

C# Static property in non-static class as indicator of main object of this class

I have class Important and some objects of this class created. I want allow user to choose main object of this class. Have a look at code below:
public class Program
{
public static void Main(string[] args)
{
Important imp1 = new Important("Important 1");
Important imp2 = new Important("Important 2");
Important imp3 = new Important("Important 3");
imp2.SetMostImportant();
Console.Write(Important.MostImportant.Name);
}
public class Important
{
public Important(string name)
{
Name = name;
if(MostImportant == null)
SetMostImportant();
}
public string Name { get; private set; }
public static Important MostImportant { get; private set; }
public void SetMostImportant()
{
MostImportant = this;
}
}
}
Is it good solution? If not, please tell me why not.
Before, to achieve this kind of things I just created boolean field named e.g. IsMainObject and, when I wanted to change main object, I iterated through all objects (or group of object) of specific class except element that I want to be main, and changed boolean to false, in my new candidate I simply set flag to true. Example below:
public class Program
{
public static void Main(string[] args)
{
Important imp1 = new Important("Important 1");
Important imp2 = new Important("Important 2");
Important imp3 = new Important("Important 3");
List<Important> list = new List<Important> { imp1, imp2, imp3 };
foreach(var item in list.Where(x => x.Name != "Important 2"))
{
item.SetMostImportant(false);
}
imp2.SetMostImportant(true);
Console.Write(list.FirstOrDefault(x => x.MostImportant == true).Name);
}
public class Important
{
public Important(string name)
{
Name = name;
}
public string Name { get; private set; }
public bool MostImportant { get; private set; }
public void SetMostImportant(bool val)
{
MostImportant = val;
}
}
}
I don't like this solution because:
I don't know if MostImportant is true for more than one objects without iterating.
I need to write extra-code to handle much more cases.
I don't have possibility to always iterate through all instances of specific class (groups not always are enough).
... and much more, but you got the idea.
public static Important MostImportant { get; private set; }
is a fine solution, and much better than
public bool MostImportant { get; private set; }
It's not uncommon to have a static property of the type that it's inside of when implementing "singleton" classes. I've written code that resembles this:
class MyClass
{
public static MyClass Instance { get; private set; }
public MyClass()
{
if (Instance == null)
{
Instance = this;
}
else
{
throw new Exception("MyClass already instantiated.");
}
}
}

How does Class A remove the item from the list based on the behavior of class B?

Question:
When a book borrowed, the Library will know automatically and
remove it from the list.
Code (C#):
public class Library
{
public string Name { get; set; }
public List<Book> BookList { get; set; }
public Library()
{
BookList = new List<Book>();
BookList.Add(new Book("HR"));
BookList.Add(new Book("HR"));
BookList.Add(new Book("Tiger"));
BookList.Add(new Book("Lion"));
BookList.Add(new Book("Elephant"));
}
}
public class Book
{
public string Name { get; set; }
public Book(string name)
{
Name = name;
}
}
Hi I have spent a lot of time to solve this question but I have no idea at all.
Do I need to use delegate event? If I use delegate event, how Library can know which item to remove?
I am not familiar with delegate. I googled around but I still can't understand how to use delegate in this question.
Or this question can be solved in other way without delegate or event?
you are on the right way. Here is an Example how to solve your requiremtns with event and delegate:
public class Library
{
public string Name { get; set; }
public List<Book> BookList { get; set; }
public Library()
{
string[] books = new string[5] { "HR", "HR", "Tiger","Lion", "Elephant" };
BookList = new List<Book>();
foreach (string s in books) {
Book b = new Book(s);
b.borrowed += borrowed;
BookList.Add(b);
}
}
private void borrowed(Book b)
{
BookList.Remove(b);
}
}
public class Book
{
public delegate void BorrowedEventHandler(Book b);
public event BorrowedEventHandler borrowed;
public string Name { get; set; }
public Book(string name)
{
Name = name;
}
public void borrow()
{
borrowed(this);
}
}
Take a look at the Decorator design pattern here: http://www.dofactory.com/net/decorator-design-pattern. Scroll down to the 'Real World code'. I believe this will help without the requirement of a delegate.

How to use generic delegates in C#

I have these classes:
public interface IPerson
{
string Name { get; set; }
}
public class Person : IPerson
{
public string Name { get; set; }
}
public interface IRoom
{
List<Furniture> Furnitures { get; set; }
List<Person> People { get; set; }
}
public class Room : IRoom
{
public List<Furniture> Furnitures { get; set; }
public List<Person> People { get; set; }
}
public enum Furniture
{
Table,
Chair
}
And I have this extension method:
public static void Assign<T>(this IRoom sender, Func<IRoom,ICollection<T>> property, T value)
{
// How do I actually add a Chair to the List<Furniture>?
}
And I want to use it like this:
var room = new Room();
room.Assign(x => x.Furnitures, Furniture.Chair);
room.Assign(x => x.People, new Person() { Name = "Joe" });
But I have no idea how to add T to ICollection<T>.
Trying to learn generics and delegates. I know room.Furnitures.Add(Furniture.Chair) works better :)
public static void Assign<T>(this IRoom room, Func<IRoom, ICollection<T>> collectionSelector, T itemToAdd)
{
collectionSelector(room).Add(itemToAdd);
}
You don't need a Func<IRoom,ICollection<T>> here. This takes room as argument and returns ICollection<T>. ICollection<T> as a parameter is enough. Let's rewrite your code as following to make it work.
public static void Assign<T>(this IRoom sender, ICollection<T> collection, T value)
{
collection.Add(value);
}
Then call it as
room.Assign(room.Furnitures, Furniture.Chair);
room.Assign(room.People, new Person() { Name = "Joe" });
If you're not satisfied with this approach and you need your own approach only then try the following
public static void Assign<T>(this IRoom sender, Func<IRoom, ICollection<T>> property, T value)
{
property(sender).Add(value);
}
Then call it with your own syntax should work
room.Assign(x => x.Furnitures, Furniture.Chair);
room.Assign(x => x.People, new Person() { Name = "Joe" });
Note:Keep in mind you've not initialized your collections, this will result in NullReferenceException, so to get rid of it add a contructor in your Room class as follows
public Room()
{
Furnitures = new List<Furniture>();
People = new List<Person>();
}

Search through HierarchicalData with recursion

I am building a treeview with a list of ScanItem. The class of ScanItem is actually:
public class ScanItem
{
public string FullPath { get; set; }
public string Name
{
get
{
return Path.GetFileName(FullPath);
}
}
public DateTime ModifiedDate { get; set; }
public DateTime CreatedDate { get; set; }
public FileAttributes Attributes { get; set; }
public bool IsDirectory { get; set; }
public string Extension
{
get
{
if (IsDirectory)
return "Folder";
else
return Path.GetExtension(Name);
}
}
public UInt64 Size { get; set; }
}
In order for me to create a treeview I needed to create two other classes in order to distinguish the folders and files in my treeview:
public class ScanFile : ScanItem
{
}
public class ScanDir : ScanItem
{
public List<ScanItem> Items { get; set; }
public ScanDir()
{
Items = new List<ScanItem>();
}
}
Note that the class ScanFile is just like the ScanItem and the ScanDir class has an extra property called Items and will contain a list of items of itself.
So if I where to iterate through this direcotory (C:\Temp):
my List will actually contain:
note that if I expand one ScanDir object I will get another List:
in order to populate the following treeview:
So I was able to populate this list using recursion by searching for files and directories in a specific path.
I just wanted to explain my situation because there are several places in the internet that enable you to filter a treeview and that is what I actually want to do. But it will be nice if I can iterate through each item in List and then remove it if some criteria is not met:
I have actually tried using the following recursive method to filter my results.
public List<ScanItem> search(List<ScanItem> items)
{
var filter = new List<ScanItem>();
foreach (var item in items)
{
if (!item.FullPath.Contains("stringIwantToLookFor")) continue;
filter.Add(item);
if (item.IsDirectory)
{
search(((ScanDir)item).Items);
}
}
return filter;
}
I think that if an item is found I need to add all the parent root directories and that's why it does not work. The reason why I want to build my own recursion method is because I want to be able to filter the treeview based on spesific criteria.
EDIT:
In other words if I want to have all the items that contain "X.txt" in my listview I want to just see:
I would do it like this: create public abstract ScanItem Seach(string s) on your ScanItem. You can then call it with the string you want to search for.
The actual implementation would look like this:
ScanFile:
public override ScanItem Seach(string s)
{
if (Name.Contains(s))
return this;
return null;
}
ScanDir:
public override ScanItem Seach(string s)
{
var results = Items.Select(i => i.Seach(s)).Where(i => i != null).ToList();
if (results.Any())
{
var result = (ScanDir)MemberwiseClone();
result.Items = results;
return result;
}
return null;
}
The implementation in ScanFile is easy: if the file matches, return it, else return null. In ScanDir, call Search on all child items recursively. If any of them returned non-null, create a copy of the current object and set the Items of the copy only to those that matched. If none matched, return null.
Note that this will search only through the names of files, not directories. But if you want to do that, such modification is going to be straight-forward.
You should treat the directories a little different because now, if the root directory does not meet the criteria the routine will exit immediately.
Try this: change your ScanItem a little:
public class ScanItem {
...
public virtual bool IsDirectory { get; }
...
}
add this to your scanFile:
public class ScanFile : ScanItem {
public override bool IsDirectory {
get { return false; }
}
}
and this to your scanDir:
public class ScanDir : ScanItem {
public List<ScanItem> Items { get; set; }
public ScanDir() {
Items = new List<ScanItem>();
}
public ScanDir CopyWithoutChildren() {
return new ScanDir() {
FullPath = this.FullPath,
ModifiedDate = this.ModifiedDate,
CreatedDate = this.CreatedDate,
Attributes = this.Attributes,
Size = this.Size
};
}
public override bool IsDirectory {
get { return true; }
}
}
Now do the filtering on the files, omitting empty directories:
public List<ScanItem> search(List<ScanItem> items) {
var filter = new List<ScanItem>();
foreach(var item in items) {
if(item.IsDirectory) {
List<ScanItem> potential = search(((ScanDir)item).Items);
if(potential.Count > 0) {
ScanDir dir = ((ScanDir)item).CopyWithoutChildren();
dir.Items.AddRange(potential);
filter.Add(dir);
}
} else {
if(!item.FullPath.Contains("stringIwantToLookFor")) continue;
filter.Add(item);
}
}
return filter;
}
I didn't test it, but I guess that should do what you want.
I realized my comment to your post might not have been descriptive enough, so I've written some C#-ish pseudocode to demonstrate what I was getting at.
Here's an example of using the Visitor pattern to implement search in a polymorphic, loosely-coupled way:
interface FilesystemVistor
{
void Visit (FilesystemItem item);
}
interface FilesystemItem
{
void Accept(FilesystemVistor visitor);
string Name;
}
class Directory : FilesystemItem
{
private FilesystemItem[] _children;
public void Accept(FilesystemVistor visitor) {
visitor.Visit(this);
foreach(FilesystemItem item in _children)
{
visitor.Visit(item);
}
}
}
class File : FilesystemItem
{
public void Accept(FilesystemVistor visitor) {
visitor.Visit(this);
}
}
class FilesystemSearcher : FilesystemVistor
{
private List<string> _results;
public void Visit(FilesystemItem item) {
if (item.Name == "Foo") { _results.Add(item.Name); }
}
}
This "visitor pattern"-based design will allow you to implement any kind of search without having the search algorithm having to "know" anything about the structure of the file system and the file system doesn't need an extra property like "IsDirectory" to expose its implementation details.
so If I am looking for the files that contain foo this method will populate the files that contain foo in the list 'newList' . I would have to set that list equal to a new list before calling that method. I am obviously missing basic implementation such as changing foo for a parameter etc. I am also missing to remove the empty directories I am working on that.
private List<ScanDir> history = new List<ScanDir>();
private ScanDir LastDir;
private List<ScanItem> newList = new List<ScanItem>();
public void Search(List<ScanItem> allItems) //adds files that contain foo
{
bool updateLastDir = false;
foreach(ScanItem s in allItems)
{
if (updateLastDir)
{
history = (from a in history
select a).Distinct().ToList();
LastDir = null;
for (int i = history.Count - 1; i >= 0; i--)
{
if (history[i].FullPath == Directory.GetParent(s.FullPath).ToString())
{
LastDir = history[i];
break;
}
}
updateLastDir = false;
}
if (s.IsDirectory)
{
var temp = new ScanDir { FullPath = s.FullPath, IsDirectory = true, comparePath = s.comparePath, Attributes = s.Attributes };
if (LastDir == null)
{
newList.Add(temp);
}
else
{
LastDir.Items.Add(temp);
}
LastDir = temp;
history.Add(LastDir);
Search(((ScanDir)s).Items);
history.RemoveAt(history.Count - 1);
updateLastDir = true;
}
else
{
if (s.Name.Contains("Foo")) // then add it
{
if (LastDir == null)
newList.Add(s);
else
LastDir.Items.Add(s);
}
}
}
}

Categories