diff --git a/csharp-methods/VirtualMethodsInCSharp/Benchmarks/Benchmarks.csproj b/csharp-methods/VirtualMethodsInCSharp/Benchmarks/Benchmarks.csproj new file mode 100644 index 0000000000..62be4f867f --- /dev/null +++ b/csharp-methods/VirtualMethodsInCSharp/Benchmarks/Benchmarks.csproj @@ -0,0 +1,14 @@ + + + + Exe + net10.0 + enable + enable + + + + + + + diff --git a/csharp-methods/VirtualMethodsInCSharp/Benchmarks/Program.cs b/csharp-methods/VirtualMethodsInCSharp/Benchmarks/Program.cs new file mode 100644 index 0000000000..9934f132e6 --- /dev/null +++ b/csharp-methods/VirtualMethodsInCSharp/Benchmarks/Program.cs @@ -0,0 +1,4 @@ +using BenchmarkDotNet.Running; +using Benchmarks; + +BenchmarkRunner.Run(); diff --git a/csharp-methods/VirtualMethodsInCSharp/Benchmarks/Shapes.cs b/csharp-methods/VirtualMethodsInCSharp/Benchmarks/Shapes.cs new file mode 100644 index 0000000000..35535fefe4 --- /dev/null +++ b/csharp-methods/VirtualMethodsInCSharp/Benchmarks/Shapes.cs @@ -0,0 +1,36 @@ +namespace Benchmarks; + +public interface IShape +{ + double CalculateArea(); +} + +public class Shape : IShape +{ + public virtual double CalculateArea() => 0; +} + +public class Circle : Shape +{ + public double Radius { get; init; } + + public override double CalculateArea() => Math.PI * Radius * Radius; + + public double CalculateAreaNonVirtual() => Math.PI * Radius * Radius; +} + +public sealed class SealedCircle : Shape +{ + public double Radius { get; init; } + + public override double CalculateArea() => Math.PI * Radius * Radius; +} + +public class Rectangle : Shape +{ + public double Width { get; init; } + + public double Height { get; init; } + + public override double CalculateArea() => Width * Height; +} diff --git a/csharp-methods/VirtualMethodsInCSharp/Benchmarks/VirtualCallBenchmarks.cs b/csharp-methods/VirtualMethodsInCSharp/Benchmarks/VirtualCallBenchmarks.cs new file mode 100644 index 0000000000..2bf5e784d5 --- /dev/null +++ b/csharp-methods/VirtualMethodsInCSharp/Benchmarks/VirtualCallBenchmarks.cs @@ -0,0 +1,82 @@ +using BenchmarkDotNet.Attributes; + +namespace Benchmarks; + +public class VirtualCallBenchmarks +{ + private const int Count = 1_000; + + private Circle[] _circles = []; + private Shape[] _circlesAsShapes = []; + private Shape[] _mixedShapes = []; + private SealedCircle[] _sealedCircles = []; + private IShape[] _circlesAsInterfaces = []; + private Func[] _delegates = []; + + [GlobalSetup] + public void Setup() + { + _circles = Enumerable.Range(1, Count).Select(i => new Circle { Radius = i }).ToArray(); + _circlesAsShapes = _circles; + _circlesAsInterfaces = _circles; + _sealedCircles = Enumerable.Range(1, Count).Select(i => new SealedCircle { Radius = i }).ToArray(); + _mixedShapes = Enumerable.Range(1, Count) + .Select(i => i % 2 == 0 ? (Shape)new Circle { Radius = i } : new Rectangle { Width = i, Height = 2 }) + .ToArray(); + _delegates = _circles.Select(c => (Func)c.CalculateArea).ToArray(); + } + + [Benchmark(Baseline = true)] + public double NonVirtual() + { + var total = 0.0; + foreach (var circle in _circles) + total += circle.CalculateAreaNonVirtual(); + return total; + } + + [Benchmark] + public double VirtualOneType() + { + var total = 0.0; + foreach (var shape in _circlesAsShapes) + total += shape.CalculateArea(); + return total; + } + + [Benchmark] + public double VirtualTwoTypes() + { + var total = 0.0; + foreach (var shape in _mixedShapes) + total += shape.CalculateArea(); + return total; + } + + [Benchmark] + public double VirtualOnSealedType() + { + var total = 0.0; + foreach (var circle in _sealedCircles) + total += circle.CalculateArea(); + return total; + } + + [Benchmark] + public double Interface() + { + var total = 0.0; + foreach (var shape in _circlesAsInterfaces) + total += shape.CalculateArea(); + return total; + } + + [Benchmark] + public double Delegate() + { + var total = 0.0; + foreach (var calculateArea in _delegates) + total += calculateArea(); + return total; + } +} diff --git a/csharp-methods/VirtualMethodsInCSharp/Tests/Tests.csproj b/csharp-methods/VirtualMethodsInCSharp/Tests/Tests.csproj index 5bc695c747..b871eec308 100644 --- a/csharp-methods/VirtualMethodsInCSharp/Tests/Tests.csproj +++ b/csharp-methods/VirtualMethodsInCSharp/Tests/Tests.csproj @@ -1,7 +1,7 @@ - net7.0 + net10.0 enable enable @@ -10,11 +10,11 @@ - - - - - + + + + + diff --git a/csharp-methods/VirtualMethodsInCSharp/Tests/VirtualMethodsInCSharpUnitTests.cs b/csharp-methods/VirtualMethodsInCSharp/Tests/VirtualMethodsInCSharpUnitTests.cs index 6f59178904..6402a40744 100644 --- a/csharp-methods/VirtualMethodsInCSharp/Tests/VirtualMethodsInCSharpUnitTests.cs +++ b/csharp-methods/VirtualMethodsInCSharp/Tests/VirtualMethodsInCSharpUnitTests.cs @@ -96,5 +96,18 @@ public void WhenCircleClassGetShapeTypeMock_ThenCircleClassGetShapeTypeResult() Assert.AreEqual("This is a circle", circleType); } + + [TestMethod] + public void WhenCircleClassDrawMock_ThenNotSupportedException() + { + var circleMock = new Mock(); + + var exception = Assert.ThrowsExactly( + () => circleMock.Setup(p => p.Draw()).Returns("Drawing a circle")); + + StringAssert.Contains(exception.Message, "Unsupported expression: p => p.Draw()"); + StringAssert.Contains(exception.Message, + "Non-overridable members (here: Circle.Draw) may not be used in setup / verification expressions."); + } } } \ No newline at end of file diff --git a/csharp-methods/VirtualMethodsInCSharp/VirtualMethodsInCSharp.sln b/csharp-methods/VirtualMethodsInCSharp/VirtualMethodsInCSharp.sln index d69b38b9ba..4cfea7802d 100644 --- a/csharp-methods/VirtualMethodsInCSharp/VirtualMethodsInCSharp.sln +++ b/csharp-methods/VirtualMethodsInCSharp/VirtualMethodsInCSharp.sln @@ -7,20 +7,54 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VirtualMethodsInCSharp", "V EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{92452288-07FB-4BBD-8D38-12051459DBA3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks", "Benchmarks\Benchmarks.csproj", "{A9E1E7FD-1F81-43D8-83AA-9F9305271A28}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {03E9D30B-AFB6-4528-925F-E9482515DC58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {03E9D30B-AFB6-4528-925F-E9482515DC58}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03E9D30B-AFB6-4528-925F-E9482515DC58}.Debug|x64.ActiveCfg = Debug|Any CPU + {03E9D30B-AFB6-4528-925F-E9482515DC58}.Debug|x64.Build.0 = Debug|Any CPU + {03E9D30B-AFB6-4528-925F-E9482515DC58}.Debug|x86.ActiveCfg = Debug|Any CPU + {03E9D30B-AFB6-4528-925F-E9482515DC58}.Debug|x86.Build.0 = Debug|Any CPU {03E9D30B-AFB6-4528-925F-E9482515DC58}.Release|Any CPU.ActiveCfg = Release|Any CPU {03E9D30B-AFB6-4528-925F-E9482515DC58}.Release|Any CPU.Build.0 = Release|Any CPU + {03E9D30B-AFB6-4528-925F-E9482515DC58}.Release|x64.ActiveCfg = Release|Any CPU + {03E9D30B-AFB6-4528-925F-E9482515DC58}.Release|x64.Build.0 = Release|Any CPU + {03E9D30B-AFB6-4528-925F-E9482515DC58}.Release|x86.ActiveCfg = Release|Any CPU + {03E9D30B-AFB6-4528-925F-E9482515DC58}.Release|x86.Build.0 = Release|Any CPU {92452288-07FB-4BBD-8D38-12051459DBA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {92452288-07FB-4BBD-8D38-12051459DBA3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {92452288-07FB-4BBD-8D38-12051459DBA3}.Debug|x64.ActiveCfg = Debug|Any CPU + {92452288-07FB-4BBD-8D38-12051459DBA3}.Debug|x64.Build.0 = Debug|Any CPU + {92452288-07FB-4BBD-8D38-12051459DBA3}.Debug|x86.ActiveCfg = Debug|Any CPU + {92452288-07FB-4BBD-8D38-12051459DBA3}.Debug|x86.Build.0 = Debug|Any CPU {92452288-07FB-4BBD-8D38-12051459DBA3}.Release|Any CPU.ActiveCfg = Release|Any CPU {92452288-07FB-4BBD-8D38-12051459DBA3}.Release|Any CPU.Build.0 = Release|Any CPU + {92452288-07FB-4BBD-8D38-12051459DBA3}.Release|x64.ActiveCfg = Release|Any CPU + {92452288-07FB-4BBD-8D38-12051459DBA3}.Release|x64.Build.0 = Release|Any CPU + {92452288-07FB-4BBD-8D38-12051459DBA3}.Release|x86.ActiveCfg = Release|Any CPU + {92452288-07FB-4BBD-8D38-12051459DBA3}.Release|x86.Build.0 = Release|Any CPU + {A9E1E7FD-1F81-43D8-83AA-9F9305271A28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9E1E7FD-1F81-43D8-83AA-9F9305271A28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9E1E7FD-1F81-43D8-83AA-9F9305271A28}.Debug|x64.ActiveCfg = Debug|Any CPU + {A9E1E7FD-1F81-43D8-83AA-9F9305271A28}.Debug|x64.Build.0 = Debug|Any CPU + {A9E1E7FD-1F81-43D8-83AA-9F9305271A28}.Debug|x86.ActiveCfg = Debug|Any CPU + {A9E1E7FD-1F81-43D8-83AA-9F9305271A28}.Debug|x86.Build.0 = Debug|Any CPU + {A9E1E7FD-1F81-43D8-83AA-9F9305271A28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9E1E7FD-1F81-43D8-83AA-9F9305271A28}.Release|Any CPU.Build.0 = Release|Any CPU + {A9E1E7FD-1F81-43D8-83AA-9F9305271A28}.Release|x64.ActiveCfg = Release|Any CPU + {A9E1E7FD-1F81-43D8-83AA-9F9305271A28}.Release|x64.Build.0 = Release|Any CPU + {A9E1E7FD-1F81-43D8-83AA-9F9305271A28}.Release|x86.ActiveCfg = Release|Any CPU + {A9E1E7FD-1F81-43D8-83AA-9F9305271A28}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/csharp-methods/VirtualMethodsInCSharp/VirtualMethodsInCSharp/Program.cs b/csharp-methods/VirtualMethodsInCSharp/VirtualMethodsInCSharp/Program.cs index 2df4726a72..90c8e48077 100644 --- a/csharp-methods/VirtualMethodsInCSharp/VirtualMethodsInCSharp/Program.cs +++ b/csharp-methods/VirtualMethodsInCSharp/VirtualMethodsInCSharp/Program.cs @@ -5,7 +5,7 @@ var rectangle = new Rectangle() { Height = 2, Width = 3 }; Console.WriteLine($"Shape area: {shape.CalculateArea()}"); // Shape area: 0 -Console.WriteLine($"Circle area: {circle.CalculateArea()}"); // Circle area: 12,566370614359172 +Console.WriteLine($"Circle area: {circle.CalculateArea()}"); // Circle area: 12.566370614359172 Console.WriteLine($"Rectangle area: {rectangle.CalculateArea()}"); // Rectangle area: 6 Console.WriteLine($"Shape type: {shape.GetShapeType()}"); // Shape type: This is a generic shape diff --git a/csharp-methods/VirtualMethodsInCSharp/VirtualMethodsInCSharp/VirtualMethodsInCSharp.csproj b/csharp-methods/VirtualMethodsInCSharp/VirtualMethodsInCSharp/VirtualMethodsInCSharp.csproj index f02677bf64..dfb40caafc 100644 --- a/csharp-methods/VirtualMethodsInCSharp/VirtualMethodsInCSharp/VirtualMethodsInCSharp.csproj +++ b/csharp-methods/VirtualMethodsInCSharp/VirtualMethodsInCSharp/VirtualMethodsInCSharp.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net10.0 enable enable