22

Currently coding in C#, I wonder if there is a way to factor the code as presented below

Entity1 = GetByName("EntityName1");
Entity2 = GetByName("EntityName2");
Entity3 = GetByName("EntityName3");

The idea would be to get a single call in the code, factoring the code by placing the entities and the strings in a container and iterating on this container to get a single "GetByName()" line. Is there a way to do this?

Asieh hojatoleslami
  • 3,240
  • 7
  • 31
  • 45
Chirac
  • 418
  • 3
  • 15
  • 1
    Apart from the fact that it's off topic to provide working code, it's difficult to improve the code without more background informations. Maybe you could use a `Dictionary`. – Tim Schmelter Apr 21 '15 at 07:23
  • It depends on how you intend on using them, you can possibly use a simple for loop but its unclear what you expect do do with them afterwards – Sayse Apr 21 '15 at 07:28
  • er yeah, that is what a loop is for. – Jodrell Apr 21 '15 at 07:45

4 Answers4

27

You can use LINQ:

var names=new[]{"EntityName1","EntityName2","EntityName3",.....};
var entities=names.Select(name=>GetByName(name)).ToArray();

Without ToArray, Select will return an IEnumerable that will be reevalueated each time you enumerate it - that is, GetByName will be called each time you enumerate the enumerable.

ToArray() or ToList will create an array (or list) you can use multiple times.

You can also call ToDictionary if you want to be able to access the entities by name:

var entities=names.ToDictionary(name=>name,name=>GetByName(name));

All this assumes that the entities don't already exist or that GetByName has to do some significant work to retrieve them. If the entities exist you can simply put them in a Dictionary<String,Entity>. If the entities have a Name property you can use ToDictionary to create the dictionary in one statement:

var entities=new []{entity1,entity2,entity3,...};
var entitiesDict=entities.ToDictionary(entity=>entity.Name,entity=>entity);
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • 7
    So why not using a `Dictionary` in the first place, if they depend so much on each other? Then the method `GetByName`, the collection `names` and `entities` and this LINQ query are redundant. – Tim Schmelter Apr 21 '15 at 07:25
  • @TimSchmelter you are making several assumptions although the question *is* vague. I assume that there is a large or varying number of names and the OP doesn't want to have one line for each call - that's what is avoided by the LINQ statement. I also assume that the entity doesn't already exist or that `GetByName` does some significant work to retrieve it. – Panagiotis Kanavos Apr 21 '15 at 07:34
  • @PanagiotisKanavos: and because you need to make assuptions this questions isn't cleat at all. Code refactoring is also off-topic on So he could post it on [stackexchange.com](http://codereview.stackexchange.com/). – Tim Schmelter Apr 21 '15 at 07:36
  • A slightly cooler and more concise way of doing this is the following: `names.Select(name=>GetByName).ToArray();`. The `GetByName` method will be automatically converted to a delegate. – Ian Newson Apr 21 '15 at 10:25
  • 1
    @IanNewson You mean `names.Select(GetByName).ToArray();` This is called a [method group](http://stackoverflow.com/questions/886822/what-is-a-method-group-in-c). Tools like Resharper suggest this refactoring whenever possible. – Panagiotis Kanavos Apr 21 '15 at 10:36
  • @PanagiotisKanavos Yes, that's what I mean. – Ian Newson Apr 21 '15 at 13:44
  • As a lesser-experienced developer in C# (been using C a lot lately), I'd consider the original, repeated calls to be much more readable than LINQ as described here. The LINQ version guarantees that the same thing happens to all instances, even after multiple revisions, but it looks messy and hard to read compared to the original's obviously parallel structure. Just my $0.02 – AaronD Apr 21 '15 at 17:35
10

Do you mean something like the below (where entities is the collection of Entity1, Entity1 & Entity3):

var results = entities.Select(e => GetByName(e.Name));
RagtimeWilly
  • 5,265
  • 3
  • 25
  • 41
7

It depends on what you're looking for. If you need to set the variables in a single line, that won't work. You could play with reflection if you're dealing with fields or properties, but honestly that seems messier than what you've got already.

If the data-structure doesn't matter, and you just need the data and are able to play with it as you see so fit, I'd probably enumerate it into a dictionary. Of course, that's pretty tightly coupled to what you've got now, which looks like it's a fake implementation anyway.

If you want to do that, it's pretty straight-forward. It's your choice how you create the IEnumerable<string> that's represented below as entityNames. You could use an array initializer as I do, you could use a List<string> that you build over time, you could even yield return it in its own method.

var entityNames = new[] { "EntityName1", "EntityName2", "EntityName3" };

var dict = entityNames.ToDictionary(c => c, c => GetByName(c));

Then it's just a matter of checking those.

var entity1 = dict["EntityName1"];

Or enumerating over the dictionary.

foreach(var kvp in dict)
{
    Console.WriteLine("{0} - {1}", kvp.Key, kvp.Value);
}

But realistically, it's hard to know whether that's preferable to what you've already got.

Matthew Haugen
  • 12,916
  • 5
  • 38
  • 54
3

Ok, here is an idea.

You can declare this function.

IReadOnlyDictionary<string, T> InstantiateByName<T>(
        Func<string, T> instantiator
        params string[] names)
{
    return names.Distinct().ToDictionary(
        name => name,
        name => instantiator(name))
}

which you could call like this,

var entities = InstantiateByName(
         GetByName,
         "EntityName1",
         "EntityName2",
         "EntityName3");

To push the over-engineering to the next level,

you can install the Immutable Collections package,

PM> Install-Package Microsoft.Bcl.Immutable

and modify the function slightly,

using Microsoft.Immutable.Collections;

IReadOnlyDictionary<string, T> InstantiateByName<T>(
        Func<string, T> instantiator
        params string[] names,
        IEqualityComparer<string> keyComparer = null,
        IEqualityComparer<T> valueComparer = null)
{
    if (keyComparer == null)
    {
        keyComparer = EqualityComparer<string>.Default;
    }

    if (valueComparer == null)
    {
        valueComparer = EqualityComparer<T>.Default;
    }

    return names.ToImmutableDictionary(
        name => name,
        name => instantiator(name),
        keyComparer,
        valueComparer);
}

The function would be used in the exactly the same way. However, the caller is responsible for passing unique keys to the function but, an alternative equality comparer can be passed.

Jodrell
  • 34,946
  • 5
  • 87
  • 124