Search code examples
c#listinterfacerefactoringreusability

In C#, how can I create a reusable method where I need to access multiple list types?


I have the following two methods:

private void AddHeaderAttachment(AAttachment attachment) {
    string parentId = attachment.ParentId;
    if (this.collectionByIds.TryGetValue(attachment.Id, out MyCollection collection)) {
        if (!collection.AAttachmentByParentIds.TryGetValue(parentId, out List<AAttachment> attachmentList)) {
            attachmentList = new List<AAttachment>();
            collection.AAttachmentByParentIds.Add(parentId, attachmentList);
        }
        attachmentList.Add(attachment);
    }
}

private void AddLineAttachment(BAttachment attachment) {
    string parentId = attachment.ParentId;
    if (this.collectionByIds.TryGetValue(attachment.Id, out MyCollection collection)) {
        if (!collection.BAttachmentByParentIds.TryGetValue(parentId, out List<BAttachment> attachmentList)) {
            attachmentList = new List<BAttachment>();
            collection.BAttachmentByParentIds.Add(parentId, attachmentList);
        }
        attachmentList.Add(attachment);
    }
}

The class MyCollection looks like this:

 public class MyCollection {
        public Dictionary<string, List<AAttachment>> AAttachmentByParentIds = new Dictionary<string, List<AAttachment>>();
        public Dictionary<string, List<BAttachment>> BAttachmentByParentIds = new Dictionary<string, List<BAttachment>>();
}

As you can see, the only differences between these two methods above are:

  1. The type being provided
  2. The property of MyCollection which the attachment will be added to.

However, both AAttachment and BAttachment implement:

 interface Attachable {
    string ParentId { get; }
 }

I would like to create one reusable method.

But I have two obvious problems:

  1. If I change the inner if to:
                if (!collection.AAttachmentByParentIds.TryGetValue(parentId, out List<Attachable> attachmentList)) {
                    attachmentList = new List<Attachable>();
                    collection.lineAttachmentByParentIds.Add(parentId, attachmentList);
                }

Visual Studio informs me "Argument 2: cannot convert out List<Attachable> to out List<AttachmentA>

and

  1. How can I alter whether I am referencing AAttachmentByParentIds or BAttachmentByParentIds on the instance of MyCollection?

Solution

  • You want to be using generics:

    private void AddHeaderAttachment<TAttachment>(
        Func<MyCollection, Dictionary<string, List<TAttachment>>> attachmentByParentIdsSelector,
        TAttachment attachment) where TAttachment : Attachable
    {
        string parentId = attachment.ParentId;
        if (this.collectionByIds.TryGetValue(attachment.Id, out MyCollection collection))
        {
            var attachmentByParentIds = attachmentByParentIdsSelector(collection);
            if (!attachmentByParentIds.TryGetValue(parentId, out List<TAttachment> attachmentList))
            {
                attachmentList = new List<TAttachment>();
                attachmentByParentIds.Add(parentId, attachmentList);
            }
            attachmentList.Add(attachment);
        }
    }
    
    private void AddHeaderAttachment(AAttachment attachment) 
    {
        AddHeaderAttachment(x => x.AAttachmentByParentIds, attachment);
    }
    
    private void AddHeaderAttachment(BAttachment attachment) 
    {
        AddHeaderAttachment(x => x.BAttachmentByParentIds, attachment);
    }