The code is well structured and easy to read. Thanks for the example. Random aside: Why do so many C# coders use #region/#endregion? It really seems like a bad habit that discourages otherwise good coders from splitting their code into logical OOP silos and instead they dump too much code into a single file, and then use regions to regain some kind of order... Regions are like goto in that they don't within their own…
> discourages ... from splitting their code into logical OOP silos What you fail to see is those silos can be a prison. One of the worst experiences I consistently have with heavily "OOP-person" code is you come to a new source tree and there are so many little tiny do-nothing classes and interfaces in individually tiny insignificant files that you can't come fresh to the project and tell "where the meat is" by brows…
The other annoyance that I often encounter at the same time is terribly long and redundant variable names, accompanied by an overdose of design patterns. ("Was it the FooFactoryInterfaceAdapterList or the FooFactoryInterfaceList that particular method was in?") An example from this article's code is GetMasterVolume() vs. GetMasterVolumeMute() --- GetMasterMute() is just as descriptive, especially when the class is already named AudioManager. There's no GetApplicationVolumeMute(), instead it nicely appears as the more succinct GetApplicationMute().
I know there are IDEs which will help you go to the right file, but it's still not as easy as just scrolling through a larger one and reading linearly. That said, excessive code duplication is to be avoided and best replaced with a function; the code referenced in this article shows signs of that, as I easily saw this fragment repeated many times, among others:
ISimpleAudioVolume volume = GetVolumeObject(pid);
if (volume == null)
return;