How to make an Action run only X count of times in C#?

// Copyright © Protiguous. All Rights Reserved.
//
// This entire copyright notice and license must be retained and must be kept visible in any binaries, libraries,
// repositories, or source code (directly or derived) from our binaries, libraries, projects, solutions, or applications.
//
// All source code belongs to [email protected] unless otherwise specified or the original license has
// been overwritten by formatting. (We try to avoid it from happening, but it does accidentally happen.)
//
// Any unmodified portions of source code gleaned from other sources still retain their original license and our thanks goes to those Authors.
// If you find your code unattributed in this source code, please let us know so we can properly attribute you and include the proper licenses and/or copyrights.
// If you want to use any of our code in a commercial project, you must contact [email protected] for permission, license, and a quote.
//
// Donations, payments, and royalties are accepted via bitcoin: 1Mad8TxTqxKnMiHuZxArFvX8BuFEB9nqX2 and PayPal: [email protected]
//
// ====================================================================
// Disclaimer:  Usage of the source code or binaries is AS-IS.
// No warranties are expressed, implied, or given.
// We are NOT responsible for Anything You Do With Our Code.
// We are NOT responsible for Anything You Do With Our Executables.
// We are NOT responsible for Anything You Do With Your Computer.
// ====================================================================
//
// Contact us by email if you have any questions, helpful criticism, or if you would like to use our code in your project(s).
// For business inquiries, please contact me at [email protected]
// Our software can be found at "https://Protiguous.com/Software/"
// Our GitHub address is "https://github.com/Protiguous".
//
// File "DelegateExtensions.cs" last formatted on 2022-04-07 at 12:38 PM by Protiguous.

public static class DelegateExtensions {

	private sealed class ContextCallOnlyXTimes {
		internal Int64 CallsAllowed;

		public ContextCallOnlyXTimes( Int64 times ) {
			if ( times <= 0 ) {
				times = 0;
			}
			this.CallsAllowed = times;
		}
	}

	/// <summary>Only allow a delegate to run X times.</summary>
	/// <param name="action"></param>
	/// <param name="callsAllowed"></param>
	/// <example>var barWithBarrier = ActionBarrier(Bar, remainingCallsAllowed: 2 );</example>
	/// <remarks>Calling the delegate more often than <paramref name="callsAllowed" /> should just NOP.</remarks>
	public static Action ActionBarrier( this Action action, Int64 callsAllowed = 1 ) {
		var context = new ContextCallOnlyXTimes( callsAllowed );

		return () => {
			if ( Interlocked.Decrement( ref context.CallsAllowed ) >= 1 ) {
				action();
			}
		};
	}

	/// <summary>Only allow a delegate to run X times.</summary>
	/// <param name="action"></param>
	/// <param name="parameter"></param>
	/// <param name="callsAllowed"></param>
	/// <example>var barWithBarrier = ActionBarrier(Bar, remainingCallsAllowed: 2 );</example>
	/// <remarks>Calling the delegate more often than <paramref name="callsAllowed" /> should just NOP.</remarks>
	public static Action ActionBarrier<T>( this Action<T?> action, T? parameter, Int64 callsAllowed = 1 ) {
		var context = new ContextCallOnlyXTimes( callsAllowed );

		return () => {
			if ( Interlocked.Decrement( ref context.CallsAllowed ) >= 1 ) {
				action( parameter );
			}
		};
	}
}

Longer lines of code are faster.. eh?

I mean, I did have another programmer vehemently argue once long ago that putting more code on one line would make it “run faster.” But of course he was stark raving nuts. Trying to explain to him (with a straight face–which was challenging) how the interpreter or compiler would break that long line apart into discrete one-instruction-per-line statements–essentially identical to the result if he had gone ahead and just made the code readable instead of trying to out-clever the compiler–had no effect on him whatsoever. But I digress.

Craig Tullis on a StackOverflow answer.
Photo by Daniel on Pexels.com

Premature optimization is the root of all evil.. NOT!

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.

Famous computer scientist Donald Knuth

His quote is often clipped down to just “premature optimization is the root of all evil”, but that is NOT TRUE.

Yes, don’t micro-benchmark everything when you begin a project, but please do think ahead. And once you’ve got the program working as intended, then make it faster by profiling and looking for the bottlenecks.

Choose the proper data structures. Array? List? Hashset? Queue? Dictionary? Caching?
Take a few moments and think it over.

How often will this line of code be ran? Do I need to inline it, or pull it out into a separate method?

How to convert any object to a Boolean value in C#

        /// <summary>
        ///     <para>Returns true if <paramref name="value" /> is a true, 'Y', "yes", "true", "1", or '1'.</para>
        ///     <para>Returns false if <paramref name="value" /> is a false, 'N', "no", "false", or '0'.</para>
        ///     <para>A null will return false.</para>
        /// </summary>
        /// <param name="value"></param>
        [Pure]
        public static Boolean ToBoolean<T>( [CanBeNull] this T value ) {
            switch ( value ) {
                case null: return false;

                case Boolean b: return b;

                case Char c: return c.In( ParsingConstants.TrueChars );

                case Int32 i: return i >= 1;

                case String s when String.IsNullOrWhiteSpace( s ): return false;

                case String s: {
                    var clean = s.Trimmed();

                    if ( clean is null ) {
                        return false;
                    }

                    if ( clean.In( ParsingConstants.TrueStrings ) ) {
                        return true;
                    }

                    if ( Boolean.TryParse( clean, out var result ) ) {
                        return result;
                    }

                    break;
                }
            }

            var t = value.ToString();

            if ( !String.IsNullOrWhiteSpace( t ) ) {
                t = t.Trim();

                if ( t.In( ParsingConstants.TrueStrings ) ) {
                    return true;
                }

                if ( t.In( ParsingConstants.FalseStrings ) ) {
                    return false;
                }

                if ( Boolean.TryParse( t, out var rest ) ) {
                    return rest;
                }
            }

            return false;
        }

        [DebuggerStepThrough]
        [Pure]
        public static Boolean? ToBooleanOrNull<T>( [CanBeNull] this T value ) {
            switch ( value ) {
                case null: return default( Boolean? );

                case Boolean b: return b;

                case Char c: return c.In( ParsingConstants.TrueChars );

                case Int32 i: return i >= 1;

                case String s when String.IsNullOrWhiteSpace( s ): return default( Boolean? );

                case String s: {
                    var trimmed = s.Trimmed();

                    if ( trimmed is null ) {
                        return default( Boolean? );
                    }

                    if ( trimmed.In( ParsingConstants.TrueStrings ) ) {
                        return true;
                    }

                    if ( trimmed.In( ParsingConstants.FalseStrings ) ) {
                        return default( Boolean? );
                    }

                    if ( Boolean.TryParse( trimmed, out var result ) ) {
                        return result;
                    }

                    break;
                }
            }

            var t = value.ToString();

            if ( String.IsNullOrWhiteSpace( t ) ) {
                return default( Boolean? );
            }

            t = t.Trim();

            if ( t.In( ParsingConstants.TrueStrings ) ) {
                return true;
            }

            if ( t.In( ParsingConstants.FalseStrings ) ) {
                return default( Boolean? );
            }

            return Boolean.TryParse( t, out var rest ) ? rest : default( Boolean? );
        }

                public static Boolean ToBooleanOrThrow<T>( [CanBeNull] this T value ) =>
            value.ToBooleanOrNull() ?? throw new FormatException( $"Unable to convert {nameof( value ).SmartQuote()} [{value}] to a boolean value." );

		/// <summary>Trim the ToString() of the object; returning null if null, empty, or whitespace.</summary>
		/// <param name="self"></param>
		/// <returns></returns>
		[DebuggerStepThrough]
		[CanBeNull]
		[Pure]
		public static String? Trimmed<T>( [CanBeNull] this T self ) =>
			self switch {
				null => default( String? ),
				String s => s.Trim().NullIfEmpty(),
				var _ => self.ToString()?.Trim().NullIfEmpty()
			};


		/// <summary>Returns null if <paramref name="self" /> is <see cref="String.IsNullOrEmpty" />.</summary>
		/// <param name="self"></param>
		/// <returns></returns>
		[CanBeNull]
		[DebuggerStepThrough]
		[Pure]
		public static String? NullIfEmpty( [CanBeNull] this String? self ) => String.IsNullOrEmpty( self ) ? null : self;

		/// <summary>
		/// N, 0, no, false, fail, failed, failure, bad
		/// </summary>
		[NotNull]
		[ItemNotNull]
		public static readonly String[] FalseStrings = {
			"N", "0", "false", Boolean.FalseString, "fail", "failed", "stop", nameof( Status.Bad ), nameof( Status.Failure ), nameof( Status.No ),
			nameof( Status.Negative )
		};

		/// <summary>
		/// Y, 1
		/// </summary>
		[NotNull]
		public static readonly Char[] TrueChars = {
			'Y', '1'
		};

		/// <summary>
		/// Y, 1, yes, true, Success, good, Go, Positive, Continue
		/// </summary>
		[NotNull]
		[ItemNotNull]
		public static readonly String[] TrueStrings = {
			"Y", "1", "yes", "true", Boolean.TrueString, nameof( Status.Success ), "good", nameof( Status.Go ), nameof( Status.Positive ), nameof( Status.Continue ), nameof(Status.Okay)
		};


“Annoying Bosses” Vent

So. Our team worked to improve the performance of the company’s two 16-year old desktop C# applications.. I focused my work on the applications and on the database procedures.

I personally made the main application load in 1 second on an Intel i5 with 4GB of ram running around 3ghz.. when it used to take ~10 seconds to load on the same types of computers. Literally 10 times faster!

I also cut the codebase literally in half, coalescing logic that was duplicated across multiple methods. Also fixed all known errors (and found some traps that would have been bad for the company.. such as calculating taxes and shipping incorrectly).

I changed the main internal logic to use awaits and async where appropriate. I also added error trapping (catching & logging logic/network/user exceptions) to almost the entire codebase.

I updated the .NET framework to the latest version.

I found and fixed all possible SQL injections.. the company had had no idea that the injections could even have happened. I made the database calls clean, fast, and error free. Cached static (information that rarely changed) responses so the geographically remote branches only had to pull information once from the main database. And there used to be methods that made the same database calls information repeatedly.. sometimes a single keypress would fire off a chain of duplicate database calls.

I created unit tests.. the CIO of the company had no idea what a “unit test” was.. or how to create & use LINQ in C#. I fixed most of the Array(s) of Objects that they stuffed Controls into because they wanted a “dynamic” interface. (It was horrible!)

The rest of the team and I (mostly 40% me and 60% them) also reworked the database tables to use the proper types. For example: changing bigint or int down to tinyint where appropriate. And tinyint to ints. Datetimes to Date where the “time” part was not needed.

We normalized almost all of the tables.. removed any unused or duplicate indexes.. the applications performances’ became very fast and responsive.


I spent the entire year fixing this mess they had created with multiple so-called ‘programmers‘ over the last 16 years. I worked nearly constantly.. never spent more than a minute or two away from coding. Excluding meetings, sick days, bathroom breaks, and lunch of course. If I was at my desk, then I was working.

I also took on the task with half the salary it should have been because I liked the company. Big Mistake™ on my part.. I blindly ignored the fact that companies can and will replace you with a cheaper programmer on a whim.

And can you guess what they did on the day I fixed the last known bug? They let me “go“. As in, “See yah, now we don’t need yah!”. Didn’t even get a thank you “for your hard work and saving the company thousands of dollars!” 🗯

I was like “Really? How rude..!

But I also figured, “Okay.. they can pay someone else to keep this application maintained and updated by the newbie programmer they hired next.. the bosses’s son.”

Professional nepotism.. and that’s what is annoying.

Oh well.. I’ve updated my résumé and moved on from that nightmare of a mess we had fixed.

Thanks for reading my vent.