[Test] // <- Attribute for test method, ignore it too
        public static void A_ChangeOfValueTypePassedByValueIsNotTakeEffectInCaller()
        {
            var sourceInteger   = 5;
            var expectedInteger = sourceInteger;

            Trace.TraceInformation($"Create a variable of a value type. Type: integer, value = '{sourceInteger}'.");

            Trace.TraceInformation("Pass it to the method, which perform incrementing of passed values.");
            var changedInteger = TypesChanger.IncrementInteger(sourceInteger);

            Trace.TraceInformation(
                "Expected, that the value in the caller is not affected, but the value in the method, " +
                "which was called, is incremented. It occurs because value type is passed by value. " +
                "It means a copy of the value is passed to the called method.");

            Trace.TraceInformation(
                $"Compare the source integer with expected (Should be the same): source = '{sourceInteger}'; expected = '{expectedInteger}'.");
            sourceInteger
            .Should()
            .Be(expectedInteger);
            // ↑ 'Should', 'Be', 'BeGreaterThan' and other methods are added from
            // FluentAssertions library, we will go deeper with it later,
            // now you only need to know, that it helps us to check objects
            Trace.TraceInformation(
                $"Compare changed integer with the original (The changed value should be greater than the original): changed = '{changedInteger}'; original = '{sourceInteger}'.");
            changedInteger
            .Should()
            .BeGreaterThan(sourceInteger);
        }
        public static void B_ChangeOfFieldWithValueTypeForComplexValueTypePassedByValueIsNotTakeEffectInCaller()
        {
            var sourceTrainer   = new TrainerValueType("Dan", 2);
            var expectedTrainer = sourceTrainer;

            Trace.TraceInformation(
                $"Create a variable of a complex value type. Type: TrainerValueType, value = '{sourceTrainer}'.");

            Trace.TraceInformation("Pass it to the method, which increment property 'Experience' (value type - int).");
            var changedTrainer = TypesChanger.IncrementExperienceOfTrainer(sourceTrainer);

            Trace.TraceInformation(
                "Expected, that the value in the caller is not affected, but the value in the method, " +
                "which was called, is incremented. It occurs because the called method got a copy of a complex value type.");

            Trace.TraceInformation(
                $"Compare the source experience with expected (Should be the same): source = '{sourceTrainer.Experience}'; expected = '{expectedTrainer.Experience}'.");
            sourceTrainer.Experience
            .Should()
            .Be(expectedTrainer.Experience);

            Trace.TraceInformation(
                $"Compare changed experience with the original (The changed value should be greater than the original): changed = '{changedTrainer.Experience}'; original = '{sourceTrainer.Experience}'.");
            changedTrainer.Experience
            .Should()
            .BeGreaterThan(sourceTrainer.Experience);
        }
        public static void B_ChangeOfStringPassedByReferenceIsNotTakeEffectInCaller()
        {
            var sourceString   = "I am a source string ";
            var expectedString = sourceString;

            Trace.TraceInformation($"Create a string variable. Type: string, value = '{sourceString}'.");

            Trace.TraceInformation("Pass it to the method, which concatenate it with GUID.");
            var changedString = TypesChanger.ConcatenateStringWithGuid(sourceString);

            Trace.TraceInformation("Expected, that the value in the caller is not affected, " +
                                   "because a new copy of string will always be created.");

            Trace.TraceInformation("Compare the source string with expected (Should be the same): " +
                                   $"source = '{sourceString}'; expected = '{expectedString}'.");

            sourceString
            .Should()
            .Be(expectedString, "because a new copy of the string is created always");

            Trace.TraceInformation("The changed value should start with the original string: " +
                                   $"original = '{sourceString}'; changed = '{changedString}'.");
            changedString
            .Should()
            .StartWith(expectedString);
        }
        public static void A_ChangeOfFieldWithValueTypeForComplexReferenceTypePassedByReferenceIsTakeEffectInCaller()
        {
            var sourceTrainee   = new TraineeReferenceType("Kate", 2);
            var expectedTrainee = new TraineeReferenceType("Kate", 2);

            Trace.TraceInformation(
                $"Create a variable of a complex reference type. Type: TraineeReferenceType, value = '{sourceTrainee}'.");

            Trace.TraceInformation("Pass it to the method, which increment property 'Assessment' (value type - int).");
            var changedTrainer = TypesChanger.IncrementAssessmentOfTrainee(sourceTrainee);

            Trace.TraceInformation("Expected, that the value in the caller is incremented in the same way " +
                                   "as the value in the method, which was called." +
                                   "It occurs because an object of a reference type is passed by reference and " +
                                   "the called method did operate on the same object, which is declared in the caller.");

            Trace.TraceInformation(
                $"Compare the source assessment with expected (Assessment of a source trainee should be greater): source = '{sourceTrainee.Assessment}'; expected = '{expectedTrainee.Assessment}'.");
            sourceTrainee.Assessment
            .Should()
            .BeGreaterThan(expectedTrainee.Assessment);

            Trace.TraceInformation(
                "Compare changed assessment with the original (The changed value should be the same): " +
                $"changed = '{changedTrainer.Assessment}'; original = '{sourceTrainee.Assessment}'.");
            changedTrainer.Assessment
            .Should()
            .Be(sourceTrainee.Assessment);
        }
        public static void C_ChangeItemInArrayOfItemsWithValueType()
        {
            var sourceArray = new[] { 1, 2, 3 };

            int[] expectedArray = new int[sourceArray.Length];
            Array.Copy(sourceArray, expectedArray, sourceArray.Length);

            Trace.TraceInformation("Create an array of items with value type. " +
                                   $"Type: int[], value = '{Utilities.CreateString(sourceArray)}'.");

            Trace.TraceInformation("Pass it to the method, which increments the " +
                                   "first item by index, then it override whole array.");
            var changedArray = TypesChanger.IncrementFirstItemByIndexThenOverrideArray(sourceArray);

            Trace.TraceInformation("Expected, that only the first item is changed, not the whole array, " +
                                   "because array is passed by reference, BUT reference is passed by value. " +
                                   "When we override the whole array, we create a new reference, which can not be passed back to the caller.");

            Trace.TraceInformation(
                "Compare the source array with expected (Should not be the same, because first item is incremented): " +
                $"source = '{Utilities.CreateString(sourceArray)}'; expected = '{Utilities.CreateString(expectedArray)}'.");

            sourceArray
            .Should()
            .StartWith(++expectedArray[0])
            .And
            .EndWith(expectedArray.Skip(1).ToArray());

            Trace.TraceInformation(
                "A new array, which is created in the called method is not match to original array: " +
                $"original = '{Utilities.CreateString(sourceArray)}'; changed = '{Utilities.CreateString(changedArray)}'.");
            changedArray
            .Should()
            .NotContain(expectedArray);
        }
        public static void D_DiscoverDiferenceBetweenIndexersOfArrayAndList()
        {
            var sourceArrayOfValueType   = new[] { new TrainerValueType("Peat", 1), new TrainerValueType("Kelly", 3) };
            var expectedArrayOfValueType = new[] { new TrainerValueType("Peat", 1), new TrainerValueType("Kelly", 3) };

            Trace.TraceInformation("Create an array of items with value type TrainerValueType. " +
                                   $"Type: TrainerValueType[], value = '{Utilities.CreateString(sourceArrayOfValueType)}'.");

            Trace.TraceInformation("Pass it to the method, which increments a property of the first item by index");
            TypesChanger.IncrementExperienceOfFirstTrainerInArray(sourceArrayOfValueType);

            Trace.TraceInformation("Expected, that the first item of the source array " +
                                   "is changed in the caller (Experience is incremented)");
            sourceArrayOfValueType[0].Experience
            .Should()
            .BeGreaterThan(expectedArrayOfValueType[0].Experience);


            var sourceListOfValueType =
                new List <TrainerValueType> {
                new TrainerValueType("Mikey", 5), new TrainerValueType("Beth", 3)
            };

            Trace.TraceInformation(
                $"Create a generic list of items with value type TrainerValueType. Type: List<TrainerValueType>, value = '{Utilities.CreateString(sourceListOfValueType)}'.");

            Trace.TraceInformation(
                "Pass it to the method, which should increment a property of the first item by index");
            Action act = () => TypesChanger.IncrementExperienceOfFirstTrainerInList(sourceListOfValueType);

            act.Should()
            .Throw <Exception>();

            Trace.TraceInformation(
                "The difference occurs because indexers of an array and a list work in a different way. " +
                "Indexer of an array returns a reference to the item of an array, that's why we can modify our item and all changes will be saved by its reference. " +
                "But indexer of a list returns a copy of the item of value type. A new copy will be returned each time, we get an item by index. " +
                "That's why compiler knows, that the operation is meaningless, you will lose your changes even locally (in the same method), and compiler prevents a runtime error with a compile-time error.");
        }
        public static void C_ChangeOfFieldWithReferenceTypeForComplexValueTypePassedByValueIsNotTakeEffectInCaller()
        {
            var sourceTrainer   = new TrainerValueType("Dan", 2);
            var expectedTrainer = sourceTrainer;

            Trace.TraceInformation(
                $"Create a variable of a complex value type with empty field (type of field - reference). Type: TrainerValueType (without trainees), value = '{sourceTrainer}'.");

            Trace.TraceInformation(
                "Pass it to the method, which add trainees (referenceType type - TraineeReferenceType).");
            var changedTrainer = TypesChanger.AssignTraineesToTrainer(sourceTrainer);

            Trace.TraceInformation(
                "Expected, that the value in the caller is not affected, but the trainer in the method, " +
                "which was called, has trainees. It occurs because the called method got a copy of a trainer " +
                "no matter the field 'Trainees' is a reference type, the field does not reference to the source reference.");

            Trace.TraceInformation(
                "Compare trainees of the source trainer with expected (Should be the same and empty): " +
                $"source = '{string.Join("; ", sourceTrainer.Trainees.Select(trainee => trainee.ToString()))}'; " +
                $"expected = '{string.Join("; ", expectedTrainer.Trainees.Select(trainee => trainee.ToString()))}'.");
            // ↑ We use LINQ query here (Select method) we will go deeper with it later, just ignore it for now.

            sourceTrainer.Trainees
            .Should()
            .BeEmpty();
            sourceTrainer.Trainees
            .Should()
            .BeSameAs(expectedTrainer.Trainees);

            Trace.TraceInformation(
                "The changed trainer should have trainees: " +
                $"changed trainees = '{string.Join("; ", changedTrainer.Trainees.Select(trainee => trainee.ToString()))}'.");
            changedTrainer.Trainees
            .Should()
            .OnlyContain(trainee => trainee != null);
        }