[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); }