简体   繁体   中英

How do I validate an item that is being Added to property that is a collection?

Ok, we have the class Ticket that contains a property:

List<TaskComment> Comments;

I have it set up with a back property so that I can do some validation:

private List<TaskComment> _comments;
public List<TaskComment> Comment
{
   get
   { //stuff }
   internal set
   { //stuff }
}

Despite setting the set to internal, the Add() method is still exposed outside of the assembly. But regardless, what I want to do is set the ticketId property of a comment object as it is being added to the collection. For example:

var ticket = new TaskTicket();
var comment = new TaskComment { //initializers }
ticket.Comments.Add(comment);

--inside the ticket:
public List<TaskComment> Comments
{
   get{ //stuff }
   set
   {
      ((TaskComment)value).TicketId = this._ticketId;
   }
}

But this isn't working. It's telling me that it can't cast from TaskComment to MyLibrary.TaskComment. Which really doesn't make any sense to me. But besides that, this doesn't feel right anyway. So how exactly do I modify the incoming value/object before adding it to the class's collection?

Expose the Collection as readonly:

public IReadOnlyCollection<TaskComment> Comments
{
   get { return new ReadOnlyCollection(_comments); }
}

Using the previous implementation _comments is now exposed to the caller. To allow the client to add/remove items you'd implement Add and Remove members that add\\remove from the internal list.

public void Add(Comment comment)
{
    /* code */
    _comments.Add(comment);
}

public void Remove(Comment comment)
{
    /* code */
    _comments.Remove(comment);
}

Alternatively, you could implement your own IList providing the proper implementations for your Add and Remove methods.

There is no way you can limit pubblicly esposed List<T> class. If you don't use the ponetial of the List<T> in your app too much, I would suggest to just wrap that list.

Say:

pulbic void AddComment(TaskComment task)
{
    /*Some validation on task parameter*/

    taskComments.Add(task);
}

and remove public get acessor from the Comments property, or entire property itself (* if this is possible)

The value object in your setter is going to be of type List so when you cast it to type TaskComment, it is throwing the error. In your code you could just do

var comment = new TaskComment { //initializers } 
comment.TicketId = ticket.TicketId; //where TicketId is a public property for _ticketId
ticket.Comments.Add(comment); 

Or you could overload the Add() method to add the comment to the Comment collection

Keep the private field as a List, but change your property so that it's readonly and is IEnumerable. Then implement a specific Add method, as others have suggested, that accesses the private field. This way your callers can freely iterate over the collection but must use the add method to add to it, giving you a place to validate, etc and throw exceptions or return a success code.

Returning concrete collection types limits your options for future changes. Whereas, returning an interface allows you to change the underlying collection with no impact on the caller(s).

Newing up a ReadOnlyCollection seems wasteful when a List has implicit IEnumerable support.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM