Bye
Bye

Reputation: 768

How to use Generic to simplify duplicated code in C#

I have three classes A, B, Common.

public class A
{
    public int Id { get; set; }

    public string Name { get; set; }
}

public class B
{
    public int Id { get; set; }

    public string Name { get; set; }
}

public class Common
{
    public int Id { get; set; }

    public string Name { get; set; }

    public string Type { get; set; }
}

I want to put all A and B into a collection of Common

var result = new List<Common>();

foreach (var i in A_collection) {
    result.Add(new Common() {
        Id = i.Id,
        Name = i.Name,
        Type = "This is A"
    });
}

foreach (var i in B_collection) {
    result.Add(new Common() {
        Id = i.Id,
        Name = i.Name,
        Type = "This is B"
    });
}

Please notice that I cannot modify any code in class A, B and Common. But I can add new classes

Is there a way to use generics to simplify the code to just use one loop like this?

foreach (var i in T_collection) {
    result.Add(new Common() {
        Id = i.Id,
        Name = i.Name,
        Type = "This is T"
    });
}

Upvotes: 0

Views: 132

Answers (4)

Caius Jard
Caius Jard

Reputation: 74605

I think I'd just write an extension method pair:

    public static Common AsCommon(this A a)
    {
        return new Common()
        {
            Id = a.Id,
            Name = a.Name,
            Type = "This is A"
        };
    }

    public static Common AsCommon(this B b)
    {
        return new Common()
        {
            Id = b.Id,
            Name = b.Name,
            Type = "This is B"
        };
    }

And then e.g.:

listOfCommon.AddRange(listOfA.Select(AsCommon));

Or e.g.:

listOfCommon = listOfA.Select(AsCommon).Concat(listOfB.Select(AsCommon)).ToList();

Probably also worth pointing out that using AutoMapper can save you the hassle of writing code to copy an A or B to a new Common, because automapper can be set up to understand A->Common and then copy the matching property names. With some extra config it can also copy mismatched properties

Upvotes: 0

MarkSouls
MarkSouls

Reputation: 999

Not impossible in technical terms. First you write methods to create Common from object...

        public static Common GenerateCommon(object o)
        {
            if (o is A)
            {
                return GenerateCommon(o as A);
            }
            if (o is B)
            {
                return GenerateCommon(o as B);
            }
            throw new Exception("Object is neither A nor B");
        }

        public static Common GenerateCommon(A a)
        {
            return new Common()
            {
                Id = a.Id,
                Name = a.Name,
                Type = "This is A"
            };
        }

        public static Common GenerateCommon(B b)
        {
            return new Common()
            {
                Id = b.Id,
                Name = b.Name,
                Type = "This is B"
            };
        }

Then concatenate two lists as list of objects.

            List<A> alist = new List<A>();
            alist.Add(new A() { Id = 1, Name = "asd" });
            alist.Add(new A() { Id = 2, Name = "2dd" });
            alist.Add(new A() { Id = 3, Name = "3q" });

            List<B> blist = new List<B>();
            blist.Add(new B() { Id = 4, Name = "4asd" });
            blist.Add(new B() { Id = 5, Name = "5dd" });
            blist.Add(new B() { Id = 6, Name = "63q" });

            List<object> olist = alist.Select(x => (object)x).ToList();
            olist.AddRange(blist.Select(x => (object)x));

            List<Common> clist = new List<Common>();
            foreach(var o in olist)
            {
                clist.Add(GenerateCommon(o));
            }

But I think modifying A/B/Common or following @T.S.'s answer is better.

Upvotes: 0

TDao
TDao

Reputation: 584

If your use case allows you to use interface in the places of the objects, and A and B are not sealed class, then you can wrap A and B in your own class with an interface. Something like this:

public interface ICommon
{
  int Id { get; set; }
  string Name { get; set; }
}

public class MyA : A, ICommon {}
public class MyB : B, ICommon {}

then instead of creating new instances of A or B, you can create instances of MyA and MyB, which will allow you to work with ICommon while still being interchangeable for A or B, or wherever you declare objects of A or B, just replace them with MyA or MyB.

Upvotes: 4

T.S.
T.S.

Reputation: 19330

Use LINQ?

var lA = new List<A>();
var lB = new List<B>();

var a = lA.Select(a => new Common(){ Id = a.Id, Name = a.Name, Type = "This is A"});
var b = lB.Select(b => new Common(){ Id = b.Id, Name = b.Name, Type = "This is B"});

var all = a.Append(b).ToList();

Upvotes: 2

Related Questions