Currently I have this code:

private async Task<(List<SomeModel> data1, List<string> data2)> 
                    ProcessFileAsync(IEnumerable<FileInfo> filesInDirectory)
    var data1 = new List<SomeModel>();
    var data2 = new List<string>();
    var inDirectory = filesInDirectory as FileInfo[] ?? filesInDirectory.ToArray();
    for (var lIndex = 0; lIndex < inDirectory.Count(); lIndex++)
        var content = await File.ReadAllTextAsync(inDirectory.ElementAt(lIndex).FullName);

        var name = Path.GetFileNameWithoutExtension(inDirectory.ElementAt(lIndex).Name);

        var data1Task = GetData1Async(content, name);
        var data2Task = GetData2Async(content);

        await Task.WhenAll(data1Task, data2Task);


    return (data1, data2);

The calling code:

private async Task<(List<SomeModel> data1, List<string> data2)> BatchProcessFilesAsync()
    var batchSize = 4;

    var models
        = await GetDataFromSomewhereAsync().ConfigureAwait(false);
    var listOfRequests = new List<Task<(List<SomeModel> data1, List<string> data2)>>();

    var fileModels = models.ToList();
    for (var skip = 0; skip <= fileModels.Count(); skip += batchSize)
        var files = fileModels.Skip(skip).Take(batchSize);

    // This will run all the calls in parallel to gain some performance
    var allFinishedTasks = await 

The problem I am facing is if I need to add data3 in future, i have to add code here again. Is there a way to make this code Solid.


1 Answer 1


Let me show you how to review your code from SOLID principles point of view.

Here I try to list some general questions and give some guidance to help you determine that your current solution does violate some of the principles or not.

Single - Responsibility principle

Q: What does XYZ method do?
If your answer resembles to any of these:

  • First it ... then it ...
  • It does ... and it ...
  • It calls ... method and ... method
  • etc.

it is a good sign that your code method does too much things.

Just to make it clear this principle does NOT suggest to wrap each line into a separate function. On the contrary, it suggests to combine those that are closely related to each other to enhance cohesion. We should strive/aim for high cohesion and low coupling.

Open - Closed principle

Q: Is your XYZ method suitable for T or W type of changes?

In this principle you try to guess what will future present you. Let's suppose that XYZ can handle X, Y and Z. If you need to support T and W then do you need to modify the current code or can you for example inherit from that and override only the necessary methods?

Liskov Substitution principle

Not applicable here.

Interface Segregation principle

Q: Do I (as a consumer of your API) have to know what does each method of IXYZ do?

This principle suggests to try to think from the consumer perspective. If I want to call only X then do I need an interface which provides X, Y and Z? You can think of this principle as an extension of SRP to interfaces. If your interface contains members that are not highly coherent then you need to slip it.

Dependency Inversion principle

Q: Does your XYZ class / method depend on other concrete classes?

This principle suggests to depend on abstractions rather than on concrete types. (Remember we are aiming for loose coupling). If you depend on abstraction and not the implementation then you can switch easily without the need to modify this class (this helps OCP as well).

I hope this helped you a bit. I suggest to further read these excellent articles:


Not the answer you're looking for? Browse other questions tagged or ask your own question.