开发者

Review extended List<> functionality

I was curious if I could enhance the standard generic List<> functionality because I was fed up with writing code such as:

var list = new List<Person>{
     new Person{Name = "David", Age = 24},
     new Person{Name = "John", Age = 30}
 };
list.Add(new Person{Name = "Terry", Age = 28});

I'd prefer that T could be implicitly constructed. The best I came up with allowed me to do this with up to four object construction parameters:

  var list = new ListWithConstructor<string, int, Person>(
                (name,age) => new Person { Name = name, Age = age })
  {
       {"David", 24},
       {"John", 30}
  };          
  list.Add("Terry", 28);

This is implemented like this:

public class ListWithConstructor<T1, T> : List<T>
{
    private readonly Func<T1, T> itemConstructor;
    public ListWithConstructor(Func<T1, T> itemConstructor)
    {
        this.itemConstructor = itemConstructor;
    }

    public void Add(T1 arg1)
    {
        base.Add(itemConstructor(arg1));
    }
}

public class ListWithConstructor<T1, T2, T> : List<T>
{
    private readonly Func<T1, T2, T> itemConstructor;
    public ListWithConstructor(Func<T1, T2, T> itemConstructor)
    {
     开发者_Python百科   this.itemConstructor = itemConstructor;
    }

    public void Add(T1 arg1, T2 arg2)
    {
        base.Add(itemConstructor(arg1, arg2));
    }
}

...and so on for up to four arguments.

Obviously the other List<> constructors (taking a capacity and an IEnumerable of existing elements) can be implemented as well.

How can this be improved?


The functionality that you are looking for is (almost) already available at your fingertips:

var names = new[] { "David", "John" };
var persons = new List<Person>(names.Select(name => new Person { Name = name }));

This way you also have a clear separation of concerns; the List is not at all involved in the object construction (not even by just invoking a delegate), but is simply assigned a sequence of objects. The transformation is taken care of separately.

This can also handle the case of transforming multiple values into single objects:

public static IList<Person> GetListFromNamesAndAges(string[] names, int[] ages)
{
    if (names.Length != ages.Length)
    {
        throw new ArgumentException("names and ages must be of equal length.");
    }

    return new List<Person>(
        names.Select((name, index) =>
            new Person { Name = name, Age = ages[index] }));
}

// usage example:
var persons = GetListFromNamesAndAges(
    new[] {"David", "John"}, 
    new[] {24, 30});

In the case of merging values from exactly two lists into single objects, using Zip might give slightly cleaner code;

return names
    .Zip(ages, (name, age) => new Person {Name = name, Age = age})
    .ToList();


You shouldn't do it like this. You are violating the "Separation Of Concerns" principle.
Why don't you just declare an implicit conversion from string to Person for the case with one constructor parameter?

public class Person
{
    public Person(string name)
    {
        Name = name;
    }

    public string Name { get; set; }

    public static implicit operator Person(string name)
    {
        return new Person(name);
    }
}

var list = new List<Person>();
list.Add("Terry");

To be able to shorten the code for adding an object with multiple constructor parameters, I think you should do the following:
Use the Func you created directly in the call to Add:

Func<string, string, Person> ctor = 
    (firstName, name) => new Person { Name = name, FirstName = firstName };
list.Add(ctor("Terry", "McDouglas"));
list.Add(ctor("Jones", "Miller"));

This is a lot cleaner.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜