-2

AWS Amazon.S3.Model.PutObjectRequest is merely a 3rd-party AWS Data Transfer Object (DTO) / Plain Old C# Object (POCO) type that can be used to build a request that can be used to send requests to an S3 Bucket. (reference

The keyContentDictionary Dictionary will contain:

  • a key that will correspond to an s3 Bucket entry's objectKey
  • a value that will correspond to the s3 Bucket entry's content data.
IDictionary<string, string> keyContentDictionary = ……blah blah blah……

Since PutObjectRequest is merely a DTO/POCO, it would be easy to use the PutObjectRequest with C# Action Delegates. It's better to use Action Delegates because we can use Anonymous C# Lambda functions to pass arguments. Using Anonymous C# Lambda functions to pass arguments gives us a great amount of flexibility for the future. This in turn can reduce the number of methods in the interfaces. Essentially, it reduces the chances of having too many different Interface methods , and overloaded Interface methods with so many different method signatures.

Therefore, here is a collection of PutObjectRequest parameterized Action Delegates:

IEnumerable<Action<PutObjectRequest>> actionDelegatesCollection = new
> List<Action<PutObjectRequest>> ();

Within the for-loop code below, there are 2 important Unconventional( i.e., hacky) "circuitous" programming techniques that needs to be noted:

  1. there is some "circuitous" programming code that involves the retrieval of the Key and Value pairing. To elaborate the key and value within the dictionary are copied over to aKey and aValue variables, respectively, which are separate dedicated variables. If the C# Lambda Anonymous function was implemented in such a way that involved retrieving directly from the dictionary's entries via the loop iteration index (i.e, 'i' in case of the code below ) like so then it will throw an error at runtime that complains about index being out of bounds:

    /* more code */(r) => {
        r.Key = keyContentDictionary.Keys.ElementAt(i);
        r.ContentBody = keyContentDictionary.Values.ElementAt(i);
    };
    
    // more code....
    

    One had to resort to an Unconventional "circuitous" programming that involves copying over to the aKey and aValue variables, and then using said variables in the C# Lambda Anonymous function.

  2. The aKey and aValue variables need to be Redeclared in every loop iteration. If I did not redeclare the aforementioned variables in every loop iteration then all the actionDelegatesCollection would all only contain the very last key and content values of the dictionary (In other words, the very last key and content values would just repeatedly show up in each of the elements in the actionDelegatesCollection)

    for (int i = 0; i < keyContentDictionary.Count; i++)
    {
        string aKey = keyContentDictionary.Keys.ElementAt(i);
        string aValue = keyContentDictionary.Values.ElementAt(i);
    
        actionDelegatesCollection.Add((r) =>
        {
            r.Key = aKey;
            r.ContentBody = aValue;
        });
    }
    

Could someone please provide an elaborate theoretical explanation for why:

  1. Why is IndexOutOfRangeException being thrown when using a loop's iteration index to retrieve elements in the Dictionary from within the C# Lambda Anonymous function?

  2. Why do the aKey and aValue variables need to be re-declared in every loop iteration?

4
  • stackoverflow.com/questions/428617/what-are-closures-in-net may help Commented Feb 12, 2024 at 23:17
  • 2
    Hey there! I've tried to answer your question as best as I could, but honestly, it's really quite a difficult read. There's a lot of seemingly unrelated content, random capitalization, explanations of random things like delegates, arbitrary formatting, but very little detail about what you're actually trying to ask. I've tried my best to format it and fix it up, but I can only do so much without knowing what you're trying to say. Commented Feb 13, 2024 at 2:01
  • 3
    This question feels miscontrued. I don't mean that it's off-topic here, but rather than the bulky premise and the eventual question seem completely misaligned with one another. I'm not sure what the first part of the question (everything before the for loop) is adding of value to be able to understand the second part (the for loop and concrete questions). Commented Feb 13, 2024 at 4:10
  • 2
    Cross-posted: softwareengineering.stackexchange.com/q/451015/34181, cs.stackexchange.com/q/165536/755. Please do not post the same question on multiple sites. Commented Feb 13, 2024 at 7:03

1 Answer 1

5

This isn't how you would iterate a dictionary. For one, it does a linear walk through its keys/values on every call to .Keys.ElementAt(i), which is really wasteful. More importantly, it's just more clunky/complicated than it needs to be.

Instead, you would use a foreach loop to get a KeyValuePair<string, string> object, whose Key and Value you could then access:

foreach (var item in keyContentDictionary)
{
    actionDelegatesCollection.Add((r) =>
    {
        r.Key = item.Key;
        r.ContentBody = item.Value;
    });
}
  1. Why do the aKey and aValue variables need to be re-declared in every loop iteration?

It's because your lambda is capturing the aKey and aValue variables themselves, not their value at the time of the lambda construction.

When the variables are outside the loop, then only 1 pair of variables exists in total, and each of your lambda instances captures that one pair. Each loop iteration overwrites their value, so only the last loop iterations values "stick", which are the ones you'll see when the lambda is later invoked.

This is much the same problem that C# used to have with foreach, prior to C# 5, where they made a breaking change to the language to give it the more expected behaviour. Eric Lippert's blog post does a great job of explaining that problem, which I think will help you understand your problem.

  1. Why is IndexOutOfRangeException being thrown when using a loop's iteration index to retrieve elements in the Dictionary from within the C# Lambda Anonymous function?

Truthfully, I don't really understand the question, but I have a vague hunch, which I'll share because you might find it useful.

I suspect you're trying to capture various index values (i) in your lambdas, and trying to use them at some later point when your lambdas are invoked. The problem is that between the time you iterated the dictionary (and say, got indices from 0 to 10), the dictionary might have shrunk (to say, only 3 elements). Those captures values between 3 and 10 will all be now out-of-bounds, at the later time when the closure is invoked and tries to use them.

2
  • 2
    I have a different hunch regarding the IndexOutOfRangeException: The OP captured the variable i in the lamdas and when evaluating the lambdas, the last value of i got used, which is keyContentDictionary.Count and is just outside the range of valid indices. Commented Feb 13, 2024 at 8:15
  • @BartvanIngenSchenau ah yes, that makes sense. But ultimately, we’re not mind readers, OP will just need to improve their question. Commented Feb 13, 2024 at 12:38

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.