1

I have a C# class. (Note that this is just to illustrate the problem. The real-world code is more complex than this.)

class MyClass
{
    private readonly MyCustomData myData = new MyCustomData();
    private int flarnCount = 0;
    /* Redacted more data structures. */

    public string One                             => myData[1];
    public List<string> Evens                     => myData.Where(item => item.Key % 2 == 0).Select(item => item.Value).ToList();
    public void SetValue(int index, string value) => myData[index] = value;
    public void Flarn()                            { flarnCount+=1; myData[flarnCount] += 12; }

    /* Redacted several hundred other similar functions. */
}

It works great, except I want to be able to call it from multiple threads. No problem, I just add a line...

private readonly object mon = new object();

.. and add a lock(mon) command to each public function.

And this is the problem. I could copy-and-paste that lock command into each of the several hundred public functions, but that seems like hard work and the end result would look really ugly.

Is there a way I can tell the compiler to include the same wrapper inside all public functions?

UPDATE - I could have equally asked how to add the same try/catch block to all public functions. Please don't get hung up on the locking problem itself which is just there to illustrate the problem.

billpg
  • 3,195
  • 3
  • 30
  • 57
  • 1
    Should be possible with PostSharp – Mighty Badaboom Sep 13 '18 at 11:33
  • You need to add a lock when you are using the dictionary? – Ebraheem Sep 13 '18 at 11:38
  • 1
    In this specific case it seems like you want to lock access to the dictionary which could just be done by putting it behind a property. Or better yet you can use a `ConcurrentDictionary` that's thread safe. – juharr Sep 13 '18 at 11:38
  • 1
    not sure what you are really trying to achieve, would perhaps using [ConcurrentDictionary](https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?redirectedfrom=MSDN&view=netframework-4.7.2) as your backing field instead suffice? – Vladi Pavelka Sep 13 '18 at 11:41
  • The code in the question is just something I knocked together in a few minutes to illustrate the problem. The real code has no dictionary. – billpg Sep 13 '18 at 11:42
  • @billpg well, I would assume you are trying to synchronize access to "something" - what is that something then? – Vladi Pavelka Sep 13 '18 at 11:43
  • @VladiPavelka Various data members inside "MyClass". Integers, Strings, Lists, Sets, Custom data structures, etc. I've edited the question to clarify this. – billpg Sep 13 '18 at 11:47
  • If you can describe your case, and what you are trying to sync access to if it is not a dictionary maybe we can help you more. – Ebraheem Sep 13 '18 at 11:55
  • regarding value types (structs) & immutable reference types (string?) - your class doesn't need to synchronize access to them, the user of the class must (make consecutive read & write atomic, if needed), regarding Lists etc, you can "synchronize access" to them by using the Concurrent version of the collection instead – Vladi Pavelka Sep 13 '18 at 11:58
  • @billpg you've said "It works great, except I want to be able to call it from multiple threads" - could you please clarify what exactly prevents you from doing so? – Vladi Pavelka Sep 13 '18 at 12:05
  • @VladiPavelka Look at the Flarn() function I've just added. If there's a context switch between the two statements, the wrong item will be updated. The solution is to add a lock command to each function, but my question is how (if its possible) to get the compiler to do it. The details of what my illustrative class does are beside the point. – billpg Sep 13 '18 at 12:08
  • 1
    I do not know how you can create an auto wrapper, but for your case I would use either `ConcurrentDictionary` or I will create 2 functions to get and set dictionary values and add lock their, and all other functions and properties will use these 2 functions. – Ebraheem Sep 13 '18 at 12:13
  • _"several hundred public functions"_ the class is broken. Which makes this a x-y-question. Reconsider your architecture. – Fildor Sep 13 '18 at 13:12
  • @Fildor - Okay I admit, by "several hundred" I actually mean ten. I still don't want to copy-paste something ten times. – billpg Sep 13 '18 at 13:31
  • @billpg ok, so there is a [race condition](https://stackoverflow.com/a/34550/9678775) in your `Flarn()` function, and you want to make it [atomic](https://stackoverflow.com/a/15054186/9678775) instead. Yes, the `lock` is the correct mechanism to achieve that. No, there is no syntax sugar/compiler magic which would deliver that behavior for free, (other than smart design - creating a "synchronization" facade etc). The most I can imagine is create your own `[Atomic]` attribute decorating a function and use [PostSharp](https://www.postsharp.net) to generate the `lock` wrapper around the func impl – Vladi Pavelka Sep 13 '18 at 13:58
  • @VladiPavelka - "No, there is no syntax sugar/compiler magic which would deliver that behavior for free," That should be an answer. I'd even give you an up-vote for it. – billpg Sep 13 '18 at 14:01

1 Answers1

0

No. Not within the confines of the C# language or compiler.

If you want to automatically add this wrapper, you could use some refactoring tool or preprocessor, essentially solve the problem at the level of "source code and text processing" rather than using the compiler.

There are plenty of downsides to that approach. Personally I would be tempted to just copy-paste the wrapper into the 10 classes and call it a day.

[edit]

An approach perhaps more idiomatic for C# specifically would be to apply the "decorator" design pattern: https://refactoring.guru/design-patterns/decorator

Alex Shroyer
  • 3,499
  • 2
  • 28
  • 54