├── StaticMain ├── Program.cs ├── StaticMain.csproj └── IHelloWorld.cs ├── .ionide └── symbolCache.db ├── Loggers ├── Loggers.csproj ├── InitialLogger.cs └── ILogger.cs ├── Repositories ├── Repositories.csproj ├── IntReader.cs └── IReader.cs ├── UnitTests.Library ├── UnitTests.Library.csproj └── IRegularPolygon.cs ├── AccessModifiers ├── AccessModifiers.csproj ├── Protected │ ├── IInventoryController.cs │ ├── InventoryItem.cs │ └── FakeInventoryController.cs ├── Public │ ├── ICustomerReader.cs │ ├── Customer.cs │ └── FakeCustomerReader.cs ├── Private │ ├── CalendarReminder.cs │ ├── CalendarEvent.cs │ └── ICalendarItem.cs └── Program.cs ├── InterfaceProperties ├── InterfaceProperties.csproj ├── BadInterface │ ├── BadObject.cs │ └── IBadInterface.cs ├── AbstractClass │ ├── SquareFromAbstractClass.cs │ └── AbstractRegularPolygon.cs ├── Interface │ ├── SquareFromInterface.cs │ └── IRegularPolygon.cs └── Program.cs ├── DangerousAssumptions ├── DangeousAssumptions.csproj ├── BadInterface │ ├── IFileHandler.cs │ ├── MyFile.cs │ └── MemoryFileHandler.cs ├── SlowPerformance │ ├── IReader.cs │ ├── FibonacciReader.cs │ └── FibonacciSequence.cs └── Program.cs ├── DynamicAndDefaultImplementation ├── DynamicAndDefaultImplementation.csproj ├── Polygon │ ├── Square.cs │ ├── IRegularPolygon.cs │ └── Triangle.cs └── Program.cs ├── StaticMembers ├── DataReaders │ ├── IPeopleReader.cs │ ├── Person.cs │ ├── CSVPeopleReader.cs │ └── HardCodedPeopleReader.cs ├── People.txt ├── StaticMembers.csproj ├── Constructor │ └── IFileLoader.cs ├── Factories │ ├── ReaderFactory.cs │ └── IReaderFactory.cs └── Program.cs ├── TesterApp ├── TesterApp.csproj └── Program.cs ├── UnitTests.Tests ├── UnitTests.Tests.csproj └── IRegularPolygonTests.cs ├── LICENSE ├── README.md ├── DefaultImplementation.sln └── .gitignore /StaticMain/Program.cs: -------------------------------------------------------------------------------- 1 | namespace StaticMain 2 | { 3 | class Program 4 | { 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.ionide/symbolCache.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jeremybytes/interfaces-in-csharp-8/HEAD/.ionide/symbolCache.db -------------------------------------------------------------------------------- /Loggers/Loggers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Repositories/Repositories.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /UnitTests.Library/UnitTests.Library.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.1 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /StaticMain/StaticMain.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AccessModifiers/AccessModifiers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /InterfaceProperties/InterfaceProperties.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DangerousAssumptions/DangeousAssumptions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /InterfaceProperties/BadInterface/BadObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace InterfaceProperties 6 | { 7 | public class BadObject : IBadInterface 8 | { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /DynamicAndDefaultImplementation/DynamicAndDefaultImplementation.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.0 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /AccessModifiers/Protected/IInventoryController.cs: -------------------------------------------------------------------------------- 1 | namespace AccessModifiers.Protected 2 | { 3 | public interface IInventoryController 4 | { 5 | public void PushInventoryItem(InventoryItem item); 6 | protected InventoryItem PullInventoryItem(int id); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /AccessModifiers/Protected/InventoryItem.cs: -------------------------------------------------------------------------------- 1 | namespace AccessModifiers.Protected 2 | { 3 | public class InventoryItem 4 | { 5 | public int Id { get; } 6 | public InventoryItem(int id) 7 | { 8 | Id = id; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /StaticMembers/DataReaders/IPeopleReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace StaticMembers 4 | { 5 | public interface IPeopleReader 6 | { 7 | public IReadOnlyCollection GetPeople(); 8 | public Person GetPerson(int id); 9 | } 10 | } -------------------------------------------------------------------------------- /UnitTests.Library/IRegularPolygon.cs: -------------------------------------------------------------------------------- 1 | namespace UnitTests.Library 2 | { 3 | public interface IRegularPolygon 4 | { 5 | int NumberOfSides { get; } 6 | int SideLength { get; set; } 7 | 8 | double GetPerimeter() => NumberOfSides * SideLength; 9 | double GetArea(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /TesterApp/TesterApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /DangerousAssumptions/BadInterface/IFileHandler.cs: -------------------------------------------------------------------------------- 1 | namespace DangerousAssumptions.BadInterface 2 | { 3 | public interface IFileHandler 4 | { 5 | void Delete(string filename); 6 | 7 | void Rename(string filename, string newfilename) => 8 | System.IO.File.Move(filename, newfilename); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /DangerousAssumptions/BadInterface/MyFile.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace DangerousAssumptions.BadInterface 4 | { 5 | public class MyFile : IFileHandler 6 | { 7 | public void Delete(string filename) 8 | { 9 | if (File.Exists(filename)) 10 | File.Delete(filename); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Loggers/InitialLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Loggers 6 | { 7 | public class InitialLogger : ILogger 8 | { 9 | public void Log(LogLevel level, string message) 10 | { 11 | Console.WriteLine($"Level - {level:F}: {message}"); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /StaticMembers/People.txt: -------------------------------------------------------------------------------- 1 | 1,John,Koenig,1975/10/17,6, 2 | 2,Dylan,Hunt,2000/10/02,8, 3 | 3,Leela,Turanga,1999/3/28,8,{1} {0} 4 | 4,John,Crichton,1999/03/19,7, 5 | 5,Dave,Lister,1988/02/15,9, 6 | 6,Laura,Roslin,2003/12/8,6, 7 | 7,John,Sheridan,1994/01/26,6, 8 | 8,Dante,Montana,2000/11/01,5, 9 | 9,Isaac,Gampu,1977/09/10,4, 10 | 10,Jeremy,Awesome,1971/05/03,10,**{0} {1}** -------------------------------------------------------------------------------- /InterfaceProperties/AbstractClass/SquareFromAbstractClass.cs: -------------------------------------------------------------------------------- 1 | namespace InterfaceProperties 2 | { 3 | public class SquareFromAbstractClass : AbstractRegularPolygon 4 | { 5 | public SquareFromAbstractClass(int sideLength) : 6 | base(4, sideLength) 7 | { } 8 | 9 | public override double Area => SideLength * SideLength; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /StaticMain/IHelloWorld.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StaticMain 4 | { 5 | public interface IHelloWorld 6 | { 7 | static void Main(string[] args) 8 | { 9 | if (args?.Length == 0) 10 | Console.WriteLine("Hello World!"); 11 | else 12 | Console.WriteLine($"Hello {args[0]}!"); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /StaticMembers/StaticMembers.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | 10 | Always 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /AccessModifiers/Public/ICustomerReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace AccessModifiers.Public 4 | { 5 | public interface ICustomerReader 6 | { 7 | // Interface members default to "public" 8 | // Both of these members are "public" 9 | IReadOnlyCollection GetCustomers(); 10 | public Customer GetCustomer(int Id); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Repositories/IntReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Repositories 6 | { 7 | public class IntReader : IReader 8 | { 9 | IReadOnlyCollection IReader.GetItems() 10 | { 11 | return new List { 1, 2, 3 }; 12 | } 13 | 14 | public int GetItem(int id) 15 | { 16 | return id; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Loggers/ILogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Loggers 6 | { 7 | public enum LogLevel 8 | { 9 | None, 10 | Info, 11 | Warning, 12 | Error, 13 | } 14 | 15 | public interface ILogger 16 | { 17 | void Log(LogLevel level, string message); 18 | 19 | void Log(Exception ex) => Log(LogLevel.Error, ex.ToString()); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /InterfaceProperties/Interface/SquareFromInterface.cs: -------------------------------------------------------------------------------- 1 | namespace InterfaceProperties 2 | { 3 | public class SquareFromInterface : IRegularPolygon 4 | { 5 | public int NumberOfSides { get; } 6 | public int SideLength { get; set; } 7 | 8 | public SquareFromInterface(int sideLength) 9 | { 10 | NumberOfSides = 4; 11 | SideLength = sideLength; 12 | } 13 | 14 | public double Area => SideLength * SideLength; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Repositories/IReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Repositories 6 | { 7 | public interface IReader 8 | { 9 | protected IReadOnlyCollection GetItems(); 10 | abstract T GetItem(int id); 11 | 12 | // Note: "Save" is not an appropriate method for a reader 13 | // interface. This was just experimenting with the syntax 14 | private void Save(T item) => Console.Write(item.ToString()); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /InterfaceProperties/Interface/IRegularPolygon.cs: -------------------------------------------------------------------------------- 1 | namespace InterfaceProperties 2 | { 3 | public interface IRegularPolygon 4 | { 5 | int NumberOfSides { get; } 6 | int SideLength { get; set; } 7 | 8 | // Note: These two lines of code are equivalent: 9 | // a calculated property with only a getter. 10 | //double Perimeter { get => NumberOfSides * SideLength; } 11 | double Perimeter => NumberOfSides * SideLength; 12 | 13 | double Area { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /DangerousAssumptions/SlowPerformance/IReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace DangerousAssumptions.SlowPerformance 5 | { 6 | public interface IReader 7 | { 8 | IReadOnlyCollection GetItems(); 9 | 10 | // Default implementation like this makes assumptions 11 | // about the underlying implementation. 12 | // It also requires that *all* items are generated. 13 | T GetItemAt(int index) => GetItems().ElementAt(index); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /DynamicAndDefaultImplementation/Polygon/Square.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicAndDefaultImplementation 2 | { 3 | public class Square : IRegularPolygon 4 | { 5 | public int NumberOfSides { get; } 6 | public int SideLength { get; set; } 7 | 8 | public Square(int sideLength) 9 | { 10 | NumberOfSides = 4; 11 | SideLength = sideLength; 12 | } 13 | 14 | public double GetArea() 15 | { 16 | return SideLength * SideLength; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /StaticMembers/Constructor/IFileLoader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace StaticMembers 5 | { 6 | public interface IFileLoader 7 | { 8 | private static string fileName = "People.txt"; 9 | 10 | public static string FileData; 11 | 12 | static IFileLoader() 13 | { 14 | string filePath = AppDomain.CurrentDomain.BaseDirectory + fileName; 15 | using (var reader = new StreamReader(filePath)) 16 | { 17 | FileData = reader.ReadToEnd(); 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DynamicAndDefaultImplementation/Polygon/IRegularPolygon.cs: -------------------------------------------------------------------------------- 1 | namespace DynamicAndDefaultImplementation 2 | { 3 | public interface IRegularPolygon 4 | { 5 | int NumberOfSides { get; } 6 | int SideLength { get; set; } 7 | 8 | // Note: These two declarations for GetPerimeter are equivalent: 9 | // a method with default implementation. 10 | //double GetPerimeter() 11 | //{ 12 | // return NumberOfSides * SideLength; 13 | //} 14 | 15 | double GetPerimeter() => NumberOfSides * SideLength; 16 | 17 | double GetArea(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /DynamicAndDefaultImplementation/Polygon/Triangle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace DynamicAndDefaultImplementation 4 | { 5 | public class Triangle : IRegularPolygon 6 | { 7 | public int NumberOfSides { get; } 8 | public int SideLength { get; set; } 9 | 10 | public Triangle(int sideLength) 11 | { 12 | NumberOfSides = 3; 13 | SideLength = sideLength; 14 | } 15 | 16 | public double GetPerimeter() => NumberOfSides * SideLength; 17 | 18 | public double GetArea() => 19 | SideLength * SideLength * Math.Sqrt(3) / 4; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /TesterApp/Program.cs: -------------------------------------------------------------------------------- 1 | using Loggers; 2 | using System; 3 | 4 | namespace TesterApp 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | ILogger logger = new InitialLogger(); 11 | logger.Log(LogLevel.Warning, "Just a warning. Carry on."); 12 | 13 | try 14 | { 15 | throw new NotImplementedException("There is no implementation"); 16 | } 17 | catch (NotImplementedException ex) 18 | { 19 | logger.Log(ex); 20 | } 21 | 22 | 23 | Console.ReadLine(); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /DangerousAssumptions/SlowPerformance/FibonacciReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace DangerousAssumptions.SlowPerformance 5 | { 6 | public class FibonacciReader : IReader 7 | { 8 | public IReadOnlyCollection GetItems() 9 | { 10 | return new FibonacciSequence().ToList(); 11 | } 12 | 13 | // Comment out this method to use the default implementation 14 | // from the interface 15 | public int GetItemAt(int index) 16 | { 17 | return new FibonacciSequence().ElementAt(index); 18 | } 19 | 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /StaticMembers/DataReaders/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StaticMembers 4 | { 5 | public class Person 6 | { 7 | public int Id { get; set; } 8 | public string GivenName { get; set; } 9 | public string FamilyName { get; set; } 10 | public DateTime StartDate { get; set; } 11 | public int Rating { get; set; } 12 | public string FormatString { get; set; } 13 | 14 | public override string ToString() 15 | { 16 | if (string.IsNullOrEmpty(FormatString)) 17 | FormatString = "{0} {1}"; 18 | return string.Format(FormatString, GivenName, FamilyName); 19 | } 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /AccessModifiers/Private/CalendarReminder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AccessModifiers.Private 4 | { 5 | public class CalendarReminder : ICalendarItem 6 | { 7 | public int CalendarId { get; set; } 8 | public string Name { get; } 9 | public string Description { get; set; } 10 | public DateTime StartTime { get; set; } 11 | 12 | public CalendarItemType ItemType { get => CalendarItemType.Reminder; } 13 | public string ItemTypeDescription { get => "REMINDER!"; } 14 | 15 | public CalendarReminder(string name, TimeSpan fromNow) 16 | { 17 | Name = name; 18 | StartTime = DateTime.Now + fromNow; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DangerousAssumptions/Program.cs: -------------------------------------------------------------------------------- 1 | using DangerousAssumptions.SlowPerformance; 2 | using System; 3 | 4 | namespace DangerousAssumptions 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | IReader fibReader = new FibonacciReader(); 11 | foreach (var number in fibReader.GetItems()) 12 | Console.Write($"{number}, "); 13 | Console.WriteLine("\n----------------------------------"); 14 | int index = 5; 15 | Console.WriteLine($"Number at index {index}: {fibReader.GetItemAt(index)}"); 16 | Console.WriteLine("----------------------------------"); 17 | Console.WriteLine(); 18 | 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /AccessModifiers/Private/CalendarEvent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AccessModifiers.Private 4 | { 5 | public class CalendarEvent : ICalendarItem 6 | { 7 | public int CalendarId { get; set; } 8 | public string Name { get; } 9 | public string Description { get; set; } 10 | public DateTime StartTime { get; set; } 11 | 12 | // Implementing class cannot access private interface members 13 | // ICalendarItem.DefaultType = CalendarItemType.Event; 14 | 15 | public CalendarItemType ItemType { get => CalendarItemType.Event; } 16 | 17 | public CalendarEvent(string name, DateTime date) 18 | { 19 | Name = name; 20 | StartTime = date.Date + DateTime.Now.TimeOfDay; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /UnitTests.Tests/UnitTests.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /StaticMembers/Factories/ReaderFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StaticMembers 4 | { 5 | public class ReaderFactory 6 | { 7 | private static IPeopleReader savedReader; 8 | public static Type readerType = typeof(HardCodedPeopleReader); 9 | 10 | public static IPeopleReader GetReader() 11 | { 12 | if (savedReader?.GetType() == readerType) 13 | return savedReader; 14 | 15 | object readerObject = Activator.CreateInstance(readerType); 16 | 17 | savedReader = readerObject as IPeopleReader; 18 | if (savedReader is null) 19 | { 20 | throw new InvalidOperationException( 21 | $"readerType '{readerType}' does not implement 'IPeopleReader'"); 22 | } 23 | 24 | return savedReader; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /StaticMembers/Factories/IReaderFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StaticMembers 4 | { 5 | public interface IReaderFactory 6 | { 7 | private static IPeopleReader savedReader; 8 | public static Type readerType = typeof(HardCodedPeopleReader); 9 | 10 | public static IPeopleReader GetReader() 11 | { 12 | if (savedReader?.GetType() == readerType) 13 | return savedReader; 14 | 15 | object readerObject = Activator.CreateInstance(readerType); 16 | 17 | savedReader = readerObject as IPeopleReader; 18 | if (savedReader is null) 19 | { 20 | throw new InvalidOperationException( 21 | $"readerType '{readerType}' does not implement 'IPeopleReader'"); 22 | } 23 | 24 | return savedReader; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /AccessModifiers/Public/Customer.cs: -------------------------------------------------------------------------------- 1 | namespace AccessModifiers.Public 2 | { 3 | public class Customer 4 | { 5 | // Class members default to "private" 6 | // These fields are both "private" 7 | string _familyName; 8 | private string _givenName; 9 | 10 | public int Id { get; set; } 11 | 12 | public string FamilyName 13 | { 14 | get { return _familyName; } 15 | set 16 | { 17 | if (value == _familyName) 18 | return; 19 | _familyName = value; 20 | } 21 | } 22 | 23 | public string GivenName 24 | { 25 | get { return _givenName; } 26 | set 27 | { 28 | if (value == _givenName) 29 | return; 30 | _givenName = value; 31 | } 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /AccessModifiers/Public/FakeCustomerReader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace AccessModifiers.Public 4 | { 5 | public class FakeCustomerReader : ICustomerReader 6 | { 7 | public Customer GetCustomer(int id) 8 | { 9 | var customer = new Customer { Id = id, GivenName = "Abigail" }; 10 | 11 | // _familyName is not accessible because it is "private" 12 | // customer._familyName = "Artois"; 13 | 14 | customer.FamilyName = "Artois"; 15 | return customer; 16 | } 17 | 18 | public IReadOnlyCollection GetCustomers() 19 | { 20 | var list = new List(); 21 | list.Add(new Customer { Id = 2, GivenName = "Marcel", FamilyName = "Dallas" }); 22 | list.Add(new Customer { Id = 4, GivenName = "Ansel", FamilyName = "Meridius" }); 23 | return list; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /DangerousAssumptions/SlowPerformance/FibonacciSequence.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | 4 | namespace DangerousAssumptions.SlowPerformance 5 | { 6 | public class FibonacciSequence : IEnumerable 7 | { 8 | public IEnumerator GetEnumerator() 9 | { 10 | var value = (current: 0, next: 1); 11 | 12 | bool notOverflowed = true; 13 | while (notOverflowed) 14 | { 15 | value = (value.next, value.current + value.next); 16 | if (value.next < 0) 17 | { 18 | // this denotes that the integer field 19 | // has overflowed. 20 | notOverflowed = false; 21 | } 22 | yield return value.current; 23 | } 24 | } 25 | 26 | IEnumerator IEnumerable.GetEnumerator() 27 | { 28 | return GetEnumerator(); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /InterfaceProperties/BadInterface/IBadInterface.cs: -------------------------------------------------------------------------------- 1 | namespace InterfaceProperties 2 | { 3 | public interface IBadInterface 4 | { 5 | // Although this compiles, the setter results in a 6 | // StackOverflow as the setter calls the setter again. 7 | // There is no way to have a backing field in an interface 8 | // to allow for syntax similar to a full property. 9 | int BadMember 10 | { 11 | get => 1; 12 | set { BadMember = value; } 13 | } 14 | 15 | // Note: If there is a default implementation for "get", 16 | // then "set" cannot be abstraction (i.e., to be implemented 17 | // by the implementing class/struct). 18 | 19 | //int NotAllowed 20 | //{ 21 | // get => 20; 22 | // abstract set; 23 | //} 24 | 25 | 26 | // The same is true for an abstract "get" with a default "set", 27 | // even if the setter does nothing. 28 | 29 | //int AlsoNotAllowed 30 | //{ 31 | // abstract get; 32 | // set { } 33 | //} 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jeremy Clark 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /StaticMembers/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace StaticMembers 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | // Use default readerType 10 | DisplayPeople(); 11 | 12 | // Set readerType to valid type 13 | IReaderFactory.readerType = typeof(CSVPeopleReader); 14 | DisplayPeople(); 15 | 16 | // Set readerType to invalid type 17 | IReaderFactory.readerType = typeof(Person); 18 | DisplayPeople(); 19 | } 20 | 21 | public static void DisplayPeople() 22 | { 23 | try 24 | { 25 | IPeopleReader reader = IReaderFactory.GetReader(); 26 | Console.WriteLine(reader.GetType()); 27 | 28 | var people = reader.GetPeople(); 29 | foreach (var person in people) 30 | Console.WriteLine(person); 31 | } 32 | catch (Exception ex) 33 | { 34 | Console.WriteLine($"{ex.GetType()}:\n {ex.Message}"); 35 | } 36 | finally 37 | { 38 | Console.WriteLine("==================="); 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /InterfaceProperties/AbstractClass/AbstractRegularPolygon.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace InterfaceProperties 4 | { 5 | public abstract class AbstractRegularPolygon 6 | { 7 | private int _numberOfSides; 8 | public int NumberOfSides 9 | { 10 | get { return _numberOfSides; } 11 | set 12 | { 13 | if (value < 3) 14 | throw new ArgumentOutOfRangeException("NumberOfSides must be 3 or greater"); 15 | _numberOfSides = value; 16 | } 17 | } 18 | 19 | private int _sideLength; 20 | public int SideLength 21 | { 22 | get { return _sideLength; } 23 | set 24 | { 25 | if (value <= 0) 26 | throw new ArgumentOutOfRangeException("SideLength must be greater than 0"); 27 | _sideLength = value; 28 | } 29 | } 30 | 31 | 32 | public AbstractRegularPolygon(int numberOfSides, int sideLength) 33 | { 34 | NumberOfSides = numberOfSides; 35 | SideLength = sideLength; 36 | } 37 | 38 | public double Perimeter => NumberOfSides * SideLength; 39 | 40 | abstract public double Area { get; } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /AccessModifiers/Protected/FakeInventoryController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace AccessModifiers.Protected 6 | { 7 | public class FakeInventoryController : IInventoryController 8 | { 9 | private Dictionary inventory = new Dictionary(); 10 | 11 | // This is a public interface member that is implemented 12 | // explicitly. This item can be explicit or non-explicit. 13 | void IInventoryController.PushInventoryItem(InventoryItem item) 14 | { 15 | if (inventory.ContainsKey(item.Id)) 16 | inventory[item.Id] += 1; 17 | else 18 | inventory.Add(item.Id, 1); 19 | } 20 | 21 | // Protected interface members must be implemented explicitly. 22 | // Non-explicit implementation is not allowed. 23 | InventoryItem IInventoryController.PullInventoryItem(int id) 24 | { 25 | if (inventory.ContainsKey(id) && inventory[id] > 0) 26 | { 27 | inventory[id] -= 1; 28 | return new InventoryItem(id); 29 | } 30 | else 31 | { 32 | throw new Exception("Item not in inventory"); 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Interfaces in C# 8 2 | ------------------- 3 | This code experiments with some of the new features of interfaces in C# 8, including default implementation, access modifiers, and static members. 4 | 5 | This is a work in progress. 6 | 7 | **Articles:** 8 | [C# 8 Interfaces are a Bit of a Mess](https://jeremybytes.blogspot.com/2019/09/interfaces-in-c-8-are-bit-of-mess.html) 9 | [A Closer Look at C# 8 Interfaces](https://jeremybytes.blogspot.com/2019/09/a-closer-look-at-c-8-interfaces.html) 10 | [Properties and Default Implementation](https://jeremybytes.blogspot.com/2019/09/c-8-interfaces-properties-and-default.html) 11 | [Dangerous Assumptions in Default Implementation](https://jeremybytes.blogspot.com/2019/09/c-8-interfaces-dangerous-assumptions-in.html) 12 | ["dynamic" and Default Implementation](https://jeremybytes.blogspot.com/2019/09/c-8-interfaces-dynamic-and-default.html) 13 | [Unit Testing Default Implementation](https://jeremybytes.blogspot.com/2019/09/c-8-interfaces-unit-testing-default.html) 14 | [Public, Private, and Protected Members](https://jeremybytes.blogspot.com/2019/11/c-8-interfaces-public-private-and.html) 15 | [Static Members](https://jeremybytes.blogspot.com/2019/12/c-8-interfaces-static-members.html) 16 | [Static Main -- Why Not?](https://jeremybytes.blogspot.com/2019/12/c-8-interfaces-static-main-why-not.html) 17 | -------------------------------------------------------------------------------- /AccessModifiers/Private/ICalendarItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AccessModifiers.Private 4 | { 5 | public enum CalendarItemType 6 | { 7 | Unspecified, 8 | Event, 9 | Meeting, 10 | Reminder, 11 | } 12 | 13 | public interface ICalendarItem 14 | { 15 | public int CalendarId { get; } 16 | public string Name { get; } 17 | public string Description { get; set; } 18 | public DateTime StartTime { get; set; } 19 | 20 | // Private abstract members not allowed 21 | // private CalendarItemType DefaultType { get; } 22 | 23 | // Private members must have default implementation 24 | private CalendarItemType DefaultType { get => CalendarItemType.Unspecified; } 25 | 26 | // Private members cannot be accessed outside 27 | // of the interface, so they are useful only for 28 | // default implementations inside the interface. 29 | public CalendarItemType ItemType { get => DefaultType; } 30 | 31 | public string ItemTypeDescription 32 | { 33 | get 34 | { 35 | switch (ItemType) 36 | { 37 | case CalendarItemType.Unspecified: 38 | return ItemType.ToString(); 39 | default: 40 | return $"Calendar {ItemType.ToString()}"; 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /DangerousAssumptions/BadInterface/MemoryFileHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace DangerousAssumptions.BadInterface 5 | { 6 | public class MemoryStringFileHandler : IFileHandler 7 | { 8 | private Dictionary Files; 9 | 10 | public MemoryStringFileHandler() 11 | { 12 | Files = new Dictionary(); 13 | } 14 | 15 | public void Write(string filename, string content) 16 | { 17 | if (filename == null) 18 | throw new ArgumentNullException(nameof(filename)); 19 | 20 | if (Files.TryAdd(filename, content)) 21 | { 22 | // everything's fine 23 | } 24 | else 25 | { 26 | throw new ArgumentException($"{filename} already exists"); 27 | } 28 | } 29 | 30 | public string Read(string filename) 31 | { 32 | if (filename == null) 33 | throw new ArgumentNullException(nameof(filename)); 34 | 35 | if (Files.TryGetValue(filename, out string content)) 36 | { 37 | return content; 38 | } 39 | else 40 | { 41 | throw new ArgumentException($"{filename} is not valid"); 42 | } 43 | } 44 | 45 | public void Delete(string filename) 46 | { 47 | if (Files.ContainsKey(filename)) 48 | { 49 | Files.Remove(filename); 50 | } 51 | else 52 | { 53 | throw new ArgumentException($"{filename} is not valid"); 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /StaticMembers/DataReaders/CSVPeopleReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace StaticMembers 6 | { 7 | public class CSVPeopleReader : IPeopleReader 8 | { 9 | public IReadOnlyCollection GetPeople() 10 | { 11 | var people = ParseString(IFileLoader.FileData); 12 | return people; 13 | } 14 | 15 | public Person GetPerson(int id) 16 | { 17 | var people = GetPeople(); 18 | return people?.FirstOrDefault(p => p.Id == id); 19 | } 20 | 21 | private List ParseString(string csvData) 22 | { 23 | var people = new List(); 24 | 25 | var lines = csvData.Split(new[] { Environment.NewLine }, StringSplitOptions.None); 26 | 27 | foreach (string line in lines) 28 | { 29 | try 30 | { 31 | var elems = line.Split(','); 32 | var per = new Person() 33 | { 34 | Id = Int32.Parse(elems[0]), 35 | GivenName = elems[1], 36 | FamilyName = elems[2], 37 | StartDate = DateTime.Parse(elems[3]), 38 | Rating = Int32.Parse(elems[4]), 39 | FormatString = elems[5], 40 | }; 41 | people.Add(per); 42 | } 43 | catch (Exception) 44 | { 45 | // Skip the bad record, log it, and move to the next record 46 | // log.write("Unable to parse record", per); 47 | } 48 | } 49 | return people; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /StaticMembers/DataReaders/HardCodedPeopleReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace StaticMembers 6 | { 7 | public class HardCodedPeopleReader : IPeopleReader 8 | { 9 | public IReadOnlyCollection GetPeople() 10 | { 11 | var p = new List() 12 | { 13 | new Person() { Id=1, GivenName="John", FamilyName="Koenig", 14 | StartDate = new DateTime(1975, 10, 17), Rating=6 }, 15 | new Person() { Id=2, GivenName="Dylan", FamilyName="Hunt", 16 | StartDate = new DateTime(2000, 10, 2), Rating=8 }, 17 | new Person() { Id=3, GivenName="Leela", FamilyName="Turanga", 18 | StartDate = new DateTime(1999, 3, 28), Rating=8, 19 | FormatString = "{1} {0}" }, 20 | new Person() { Id=4, GivenName="John", FamilyName="Crichton", 21 | StartDate = new DateTime(1999, 3, 19), Rating=7 }, 22 | new Person() { Id=5, GivenName="Dave", FamilyName="Lister", 23 | StartDate = new DateTime(1988, 2, 15), Rating=9 }, 24 | new Person() { Id=6, GivenName="Laura", FamilyName="Roslin", 25 | StartDate = new DateTime(2003, 12, 8), Rating=6}, 26 | new Person() { Id=7, GivenName="John", FamilyName="Sheridan", 27 | StartDate = new DateTime(1994, 1, 26), Rating=6 }, 28 | new Person() { Id=8, GivenName="Dante", FamilyName="Montana", 29 | StartDate = new DateTime(2000, 11, 1), Rating=5 }, 30 | new Person() { Id=9, GivenName="Isaac", FamilyName="Gampu", 31 | StartDate = new DateTime(1977, 9, 10), Rating=4 }, 32 | }; 33 | return p; 34 | } 35 | 36 | public Person GetPerson(int id) 37 | { 38 | return GetPeople().FirstOrDefault(p => p.Id == id); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /InterfaceProperties/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace InterfaceProperties 4 | { 5 | class Program 6 | { 7 | static void Main(string[] args) 8 | { 9 | // "smallSquare" type is SquareFromInterface 10 | var smallSquare = new SquareFromInterface(5); 11 | ShowInterfacePolygon(smallSquare); 12 | 13 | // "largeSquare" type is IRegularPolygon 14 | IRegularPolygon largeSquare = new SquareFromInterface(20); 15 | ShowInterfacePolygon(largeSquare); 16 | 17 | // "littleSquare" type is SquareFromAbstractClass 18 | var littleSquare = new SquareFromAbstractClass(6); 19 | ShowAbstractPolygon(littleSquare); 20 | 21 | // "bigSquare" type is AbstractRegularPolygon 22 | AbstractRegularPolygon bigSquare = new SquareFromAbstractClass(21); 23 | ShowAbstractPolygon(bigSquare); 24 | 25 | // Setting "BadMember" property results in a StackOverflow 26 | IBadInterface bigError = new BadObject(); 27 | //bigError.BadMember = 2; 28 | } 29 | 30 | private static void ShowInterfacePolygon(IRegularPolygon polygon) 31 | { 32 | Console.WriteLine($"# of Sides: {polygon.NumberOfSides}"); 33 | Console.WriteLine($"Side Length: {polygon.SideLength}"); 34 | Console.WriteLine($"Perimeter: {polygon.Perimeter}"); 35 | Console.WriteLine($"Area: {polygon.Area}"); 36 | Console.WriteLine("----------------------------------"); 37 | } 38 | 39 | private static void ShowAbstractPolygon(AbstractRegularPolygon polygon) 40 | { 41 | Console.WriteLine($"# of Sides: {polygon.NumberOfSides}"); 42 | Console.WriteLine($"Side Length: {polygon.SideLength}"); 43 | Console.WriteLine($"Perimeter: {polygon.Perimeter}"); 44 | Console.WriteLine($"Area: {polygon.Area}"); 45 | Console.WriteLine("----------------------------------"); 46 | } 47 | 48 | private static void ShowDynamicPolygon(dynamic polygon) 49 | { 50 | Console.WriteLine($"# of Sides: {polygon.NumberOfSides}"); 51 | Console.WriteLine($"Side Length: {polygon.SideLength}"); 52 | 53 | // NOTE: This line fails with the interface because the 54 | // default implemented member "Perimeter" is not accessible 55 | // using "dynamic" 56 | Console.WriteLine($"Perimeter: {polygon.Perimeter}"); 57 | 58 | Console.WriteLine($"Area: {polygon.Area}"); 59 | Console.WriteLine("----------------------------------"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /AccessModifiers/Program.cs: -------------------------------------------------------------------------------- 1 | using AccessModifiers.Private; 2 | using AccessModifiers.Protected; 3 | using AccessModifiers.Public; 4 | using System; 5 | 6 | namespace AccessModifiers 7 | { 8 | class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | // Private Members 13 | ICalendarItem calendarEvent = new CalendarEvent("Dwayne's Graduation", DateTime.Today.AddMonths(1)); 14 | DisplayCalendarItem(calendarEvent); 15 | 16 | ICalendarItem reminder = new CalendarReminder("Send birthday card to Penny", new TimeSpan(5, 30, 0)); 17 | DisplayCalendarItem(reminder); 18 | 19 | 20 | // Protected Members 21 | IInventoryController controller = new FakeInventoryController(); 22 | var item = new InventoryItem(7); 23 | controller.PushInventoryItem(item); 24 | // Protected members cannot be accessed through 25 | // through the implementation. 26 | // Protected members may only be useful in interfaces 27 | // heirarchies since they cannot be accessed through 28 | // implementers. 29 | // controller.PullInventoryItem(7); 30 | 31 | 32 | // Public Members 33 | // Public members are accessible through implementers 34 | ICustomerReader reader = new FakeCustomerReader(); 35 | var customerList = reader.GetCustomers(); 36 | DisplayCustomerList(customerList); 37 | 38 | } 39 | 40 | private static void DisplayCustomerList(System.Collections.Generic.IReadOnlyCollection customerList) 41 | { 42 | Console.WriteLine("======================="); 43 | int count = 1; 44 | foreach (var customer in customerList) 45 | { 46 | Console.WriteLine($"Item #{count}"); 47 | Console.WriteLine($"Customer Id: {customer.Id}"); 48 | Console.WriteLine($"Given Name: {customer.GivenName}"); 49 | Console.WriteLine($"Family Name: {customer.FamilyName}"); 50 | Console.WriteLine("---"); 51 | count++; 52 | } 53 | Console.WriteLine("======================="); 54 | } 55 | 56 | private static void DisplayCalendarItem(ICalendarItem calendarEvent) 57 | { 58 | Console.WriteLine("======================="); 59 | Console.WriteLine("Calendar Item"); 60 | Console.WriteLine($"Name: {calendarEvent.Name}"); 61 | Console.WriteLine($"Start Time: {calendarEvent.StartTime:s}"); 62 | Console.WriteLine($"Type: {calendarEvent.ItemType}"); 63 | Console.WriteLine($"Type: {calendarEvent.ItemTypeDescription}"); 64 | Console.WriteLine("======================="); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /DynamicAndDefaultImplementation/Program.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.CSharp.RuntimeBinder; 2 | using System; 3 | 4 | namespace DynamicAndDefaultImplementation 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | // "smallSquare" type is Square 11 | var smallSquare = new Square(5); 12 | ShowInterfacePolygon(smallSquare); 13 | 14 | try 15 | { 16 | ShowDynamicPolygon(smallSquare); 17 | } 18 | catch (RuntimeBinderException ex) 19 | { 20 | // This call throws an exception because 21 | // Square does not have a "GetPerimeter" method. 22 | Console.WriteLine("----------------------------------"); 23 | Console.WriteLine("RuntimeBinderException thrown."); 24 | Console.WriteLine(ex.Message); 25 | Console.WriteLine("----------------------------------"); 26 | } 27 | 28 | // "largeSquare" type is IRegularPolygon 29 | IRegularPolygon largeSquare = new Square(20); 30 | ShowInterfacePolygon(largeSquare); 31 | 32 | try 33 | { 34 | ShowDynamicPolygon(largeSquare); 35 | } 36 | catch (RuntimeBinderException ex) 37 | { 38 | // This call throws an exception because 39 | // the runtime binder cannot see the default 40 | // "GetPerimeter" method on the interface. 41 | Console.WriteLine("----------------------------------"); 42 | Console.WriteLine("RuntimeBinderException thrown."); 43 | Console.WriteLine(ex.Message); 44 | Console.WriteLine("----------------------------------"); 45 | } 46 | 47 | // "triangle" type is IRegularPolygon 48 | IRegularPolygon triangle = new Triangle(10); 49 | ShowInterfacePolygon(triangle); 50 | ShowDynamicPolygon(triangle); 51 | } 52 | 53 | private static void ShowInterfacePolygon(IRegularPolygon polygon) 54 | { 55 | Console.WriteLine($"# of Sides: {polygon.NumberOfSides}"); 56 | Console.WriteLine($"Side Length: {polygon.SideLength}"); 57 | Console.WriteLine($"Perimeter: {polygon.GetPerimeter()}"); 58 | Console.WriteLine($"Area: {polygon.GetArea()}"); 59 | Console.WriteLine("----------------------------------"); 60 | } 61 | 62 | private static void ShowDynamicPolygon(dynamic polygon) 63 | { 64 | Console.WriteLine($"# of Sides: {polygon.NumberOfSides}"); 65 | Console.WriteLine($"Side Length: {polygon.SideLength}"); 66 | 67 | // NOTE: This line fails with the interface because the 68 | // default implemented member "GetPerimeter()" is not accessible 69 | // using "dynamic" 70 | Console.WriteLine($"Perimeter: {polygon.GetPerimeter()}"); 71 | 72 | Console.WriteLine($"Area: {polygon.GetArea()}"); 73 | Console.WriteLine("----------------------------------"); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /UnitTests.Tests/IRegularPolygonTests.cs: -------------------------------------------------------------------------------- 1 | using UnitTests.Library; 2 | using Moq; 3 | using NUnit.Framework; 4 | using NSubstitute; 5 | using FakeItEasy; 6 | 7 | namespace UnitTests.Tests 8 | { 9 | public class FakePolygonWithDefault : IRegularPolygon 10 | { 11 | public int NumberOfSides { get { return 4; } } 12 | public int SideLength { get { return 5; } set { } } 13 | public double GetArea() => SideLength * SideLength; 14 | } 15 | 16 | public class FakePolygonWithOverride : IRegularPolygon 17 | { 18 | public int NumberOfSides { get { return 4; } } 19 | public int SideLength { get { return 5; } set { } } 20 | public double GetPerimeter => NumberOfSides * SideLength; 21 | public double GetArea() => SideLength * SideLength; 22 | } 23 | 24 | 25 | public class IRegularPolygonTests 26 | { 27 | [Test] 28 | public void FakeObject_CheckDefaultImplementation() 29 | { 30 | IRegularPolygon fake = new FakePolygonWithDefault(); 31 | 32 | double result = fake.GetPerimeter(); 33 | 34 | Assert.AreEqual(20.0, result); 35 | } 36 | 37 | [Test] 38 | public void FakeObject_OverrideDefaultImplementation() 39 | { 40 | IRegularPolygon fake = new FakePolygonWithOverride(); 41 | 42 | double result = fake.GetPerimeter(); 43 | 44 | Assert.AreEqual(20.0, result); 45 | } 46 | 47 | [Test] 48 | public void Moq_CheckDefaultImplementation() 49 | { 50 | var mock = new Mock(); 51 | mock.SetupGet(m => m.NumberOfSides).Returns(3); 52 | mock.SetupGet(m => m.SideLength).Returns(5); 53 | 54 | double result = mock.Object.GetPerimeter(); 55 | 56 | Assert.AreEqual(15.0, result); 57 | } 58 | 59 | [Test] 60 | public void Moq_OverrideDefaultImplementation() 61 | { 62 | var mock = new Mock(); 63 | mock.SetupGet(m => m.NumberOfSides).Returns(3); 64 | mock.SetupGet(m => m.SideLength).Returns(5); 65 | mock.Setup(m => m.GetPerimeter()).Returns( 66 | (mock.Object.SideLength * mock.Object.NumberOfSides)); 67 | 68 | double result = mock.Object.GetPerimeter(); 69 | 70 | Assert.AreEqual(15.0, result); 71 | } 72 | 73 | [Test] 74 | public void NSubstitute_CheckDefaultImplementation() 75 | { 76 | var mock = Substitute.For(); 77 | mock.NumberOfSides.Returns(3); 78 | mock.SideLength.Returns(5); 79 | 80 | double result = mock.GetPerimeter(); 81 | 82 | Assert.AreEqual(15.0, result); 83 | } 84 | 85 | [Test] 86 | public void NSubstitute_OverrideDefaultImplementation() 87 | { 88 | var mock = Substitute.For(); 89 | mock.NumberOfSides.Returns(3); 90 | mock.SideLength.Returns(5); 91 | double perimeter = mock.NumberOfSides * mock.SideLength; 92 | mock.GetPerimeter().Returns(perimeter); 93 | 94 | double result = mock.GetPerimeter(); 95 | 96 | Assert.AreEqual(15,0, result); 97 | } 98 | 99 | [Test] 100 | public void FakeItEasy_CheckDefaultImplementation() 101 | { 102 | var mock = A.Fake(); 103 | A.CallTo(() => mock.NumberOfSides).Returns(3); 104 | A.CallTo(() => mock.SideLength).Returns(5); 105 | 106 | double result = mock.GetPerimeter(); 107 | 108 | Assert.AreEqual(15.0, result); 109 | } 110 | 111 | [Test] 112 | public void FakeItEasy_OverrideDefaultImplementation() 113 | { 114 | var mock = A.Fake(); 115 | A.CallTo(() => mock.NumberOfSides).Returns(3); 116 | A.CallTo(() => mock.SideLength).Returns(5); 117 | A.CallTo(() => mock.GetPerimeter()).Returns(mock.NumberOfSides * mock.SideLength); 118 | 119 | double result = mock.GetPerimeter(); 120 | 121 | Assert.AreEqual(15.0, result); 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /DefaultImplementation.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29123.88 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Repositories", "Repositories\Repositories.csproj", "{25176922-3ED1-4F66-8E05-E7A81DA2F97A}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Loggers", "Loggers\Loggers.csproj", "{4F11CC55-2FC6-4026-9F8A-EA5E30434DB7}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TesterApp", "TesterApp\TesterApp.csproj", "{E01FCC5B-AEA9-4974-A148-C44CF437BF58}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InterfaceProperties", "InterfaceProperties\InterfaceProperties.csproj", "{FBD2E2A1-CC0B-4486-A815-CDD3D54C7D27}" 13 | EndProject 14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicAndDefaultImplementation", "DynamicAndDefaultImplementation\DynamicAndDefaultImplementation.csproj", "{7A0DF80C-128E-4AD4-BBCE-6EFF50722F9E}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DangeousAssumptions", "DangerousAssumptions\DangeousAssumptions.csproj", "{35090D9E-4016-4E27-9556-856CB9F798B9}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests.Library", "UnitTests.Library\UnitTests.Library.csproj", "{3CA4BF7C-9EAF-44FA-9E32-C16E8843DB47}" 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTests.Tests", "UnitTests.Tests\UnitTests.Tests.csproj", "{0A5A8D16-86B8-4495-9793-CCD78386B10B}" 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AccessModifiers", "AccessModifiers\AccessModifiers.csproj", "{8604EE13-C300-4334-9107-79D871412D44}" 23 | EndProject 24 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StaticMembers", "StaticMembers\StaticMembers.csproj", "{1086C5E8-831E-44D1-A89B-093DAE62C75F}" 25 | EndProject 26 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StaticMain", "StaticMain\StaticMain.csproj", "{8DCF2FC2-6988-4E3D-821C-FA8AF433E263}" 27 | EndProject 28 | Global 29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 30 | Debug|Any CPU = Debug|Any CPU 31 | Release|Any CPU = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 34 | {25176922-3ED1-4F66-8E05-E7A81DA2F97A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {25176922-3ED1-4F66-8E05-E7A81DA2F97A}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {25176922-3ED1-4F66-8E05-E7A81DA2F97A}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {25176922-3ED1-4F66-8E05-E7A81DA2F97A}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {4F11CC55-2FC6-4026-9F8A-EA5E30434DB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {4F11CC55-2FC6-4026-9F8A-EA5E30434DB7}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {4F11CC55-2FC6-4026-9F8A-EA5E30434DB7}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {4F11CC55-2FC6-4026-9F8A-EA5E30434DB7}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {E01FCC5B-AEA9-4974-A148-C44CF437BF58}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {E01FCC5B-AEA9-4974-A148-C44CF437BF58}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {E01FCC5B-AEA9-4974-A148-C44CF437BF58}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {E01FCC5B-AEA9-4974-A148-C44CF437BF58}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {FBD2E2A1-CC0B-4486-A815-CDD3D54C7D27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {FBD2E2A1-CC0B-4486-A815-CDD3D54C7D27}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {FBD2E2A1-CC0B-4486-A815-CDD3D54C7D27}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {FBD2E2A1-CC0B-4486-A815-CDD3D54C7D27}.Release|Any CPU.Build.0 = Release|Any CPU 50 | {7A0DF80C-128E-4AD4-BBCE-6EFF50722F9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 51 | {7A0DF80C-128E-4AD4-BBCE-6EFF50722F9E}.Debug|Any CPU.Build.0 = Debug|Any CPU 52 | {7A0DF80C-128E-4AD4-BBCE-6EFF50722F9E}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {7A0DF80C-128E-4AD4-BBCE-6EFF50722F9E}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {35090D9E-4016-4E27-9556-856CB9F798B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 55 | {35090D9E-4016-4E27-9556-856CB9F798B9}.Debug|Any CPU.Build.0 = Debug|Any CPU 56 | {35090D9E-4016-4E27-9556-856CB9F798B9}.Release|Any CPU.ActiveCfg = Release|Any CPU 57 | {35090D9E-4016-4E27-9556-856CB9F798B9}.Release|Any CPU.Build.0 = Release|Any CPU 58 | {3CA4BF7C-9EAF-44FA-9E32-C16E8843DB47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {3CA4BF7C-9EAF-44FA-9E32-C16E8843DB47}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {3CA4BF7C-9EAF-44FA-9E32-C16E8843DB47}.Release|Any CPU.ActiveCfg = Release|Any CPU 61 | {3CA4BF7C-9EAF-44FA-9E32-C16E8843DB47}.Release|Any CPU.Build.0 = Release|Any CPU 62 | {0A5A8D16-86B8-4495-9793-CCD78386B10B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 63 | {0A5A8D16-86B8-4495-9793-CCD78386B10B}.Debug|Any CPU.Build.0 = Debug|Any CPU 64 | {0A5A8D16-86B8-4495-9793-CCD78386B10B}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {0A5A8D16-86B8-4495-9793-CCD78386B10B}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {8604EE13-C300-4334-9107-79D871412D44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 67 | {8604EE13-C300-4334-9107-79D871412D44}.Debug|Any CPU.Build.0 = Debug|Any CPU 68 | {8604EE13-C300-4334-9107-79D871412D44}.Release|Any CPU.ActiveCfg = Release|Any CPU 69 | {8604EE13-C300-4334-9107-79D871412D44}.Release|Any CPU.Build.0 = Release|Any CPU 70 | {1086C5E8-831E-44D1-A89B-093DAE62C75F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 71 | {1086C5E8-831E-44D1-A89B-093DAE62C75F}.Debug|Any CPU.Build.0 = Debug|Any CPU 72 | {1086C5E8-831E-44D1-A89B-093DAE62C75F}.Release|Any CPU.ActiveCfg = Release|Any CPU 73 | {1086C5E8-831E-44D1-A89B-093DAE62C75F}.Release|Any CPU.Build.0 = Release|Any CPU 74 | {8DCF2FC2-6988-4E3D-821C-FA8AF433E263}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 75 | {8DCF2FC2-6988-4E3D-821C-FA8AF433E263}.Debug|Any CPU.Build.0 = Debug|Any CPU 76 | {8DCF2FC2-6988-4E3D-821C-FA8AF433E263}.Release|Any CPU.ActiveCfg = Release|Any CPU 77 | {8DCF2FC2-6988-4E3D-821C-FA8AF433E263}.Release|Any CPU.Build.0 = Release|Any CPU 78 | EndGlobalSection 79 | GlobalSection(SolutionProperties) = preSolution 80 | HideSolutionNode = FALSE 81 | EndGlobalSection 82 | GlobalSection(ExtensibilityGlobals) = postSolution 83 | SolutionGuid = {3E51FD95-EF3E-4AF9-9DAC-61F82F464005} 84 | EndGlobalSection 85 | EndGlobal 86 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # PowerPoint files 13 | *.ppt 14 | *.pptx 15 | 16 | # User-specific files (MonoDevelop/Xamarin Studio) 17 | *.userprefs 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | bld/ 27 | [Bb]in/ 28 | [Oo]bj/ 29 | [Ll]og/ 30 | 31 | # Visual Studio 2015/2017 cache/options directory 32 | .vs/ 33 | # Uncomment if you have tasks that create the project's static files in wwwroot 34 | #wwwroot/ 35 | 36 | # Visual Studio 2017 auto generated files 37 | Generated\ Files/ 38 | 39 | # MSTest test Results 40 | [Tt]est[Rr]esult*/ 41 | [Bb]uild[Ll]og.* 42 | 43 | # NUNIT 44 | *.VisualState.xml 45 | TestResult.xml 46 | 47 | # Build Results of an ATL Project 48 | [Dd]ebugPS/ 49 | [Rr]eleasePS/ 50 | dlldata.c 51 | 52 | # Benchmark Results 53 | BenchmarkDotNet.Artifacts/ 54 | 55 | # .NET Core 56 | project.lock.json 57 | project.fragment.lock.json 58 | artifacts/ 59 | **/Properties/launchSettings.json 60 | 61 | # StyleCop 62 | StyleCopReport.xml 63 | 64 | # Files built by Visual Studio 65 | *_i.c 66 | *_p.c 67 | *_i.h 68 | *.ilk 69 | *.meta 70 | *.obj 71 | *.iobj 72 | *.pch 73 | *.pdb 74 | *.ipdb 75 | *.pgc 76 | *.pgd 77 | *.rsp 78 | *.sbr 79 | *.tlb 80 | *.tli 81 | *.tlh 82 | *.tmp 83 | *.tmp_proj 84 | *.log 85 | *.vspscc 86 | *.vssscc 87 | .builds 88 | *.pidb 89 | *.svclog 90 | *.scc 91 | 92 | # Chutzpah Test files 93 | _Chutzpah* 94 | 95 | # Visual C++ cache files 96 | ipch/ 97 | *.aps 98 | *.ncb 99 | *.opendb 100 | *.opensdf 101 | *.sdf 102 | *.cachefile 103 | *.VC.db 104 | *.VC.VC.opendb 105 | 106 | # Visual Studio profiler 107 | *.psess 108 | *.vsp 109 | *.vspx 110 | *.sap 111 | 112 | # Visual Studio Trace Files 113 | *.e2e 114 | 115 | # TFS 2012 Local Workspace 116 | $tf/ 117 | 118 | # Guidance Automation Toolkit 119 | *.gpState 120 | 121 | # ReSharper is a .NET coding add-in 122 | _ReSharper*/ 123 | *.[Rr]e[Ss]harper 124 | *.DotSettings.user 125 | 126 | # JustCode is a .NET coding add-in 127 | .JustCode 128 | 129 | # TeamCity is a build add-in 130 | _TeamCity* 131 | 132 | # DotCover is a Code Coverage Tool 133 | *.dotCover 134 | 135 | # AxoCover is a Code Coverage Tool 136 | .axoCover/* 137 | !.axoCover/settings.json 138 | 139 | # Visual Studio code coverage results 140 | *.coverage 141 | *.coveragexml 142 | 143 | # NCrunch 144 | _NCrunch_* 145 | .*crunch*.local.xml 146 | nCrunchTemp_* 147 | 148 | # MightyMoose 149 | *.mm.* 150 | AutoTest.Net/ 151 | 152 | # Web workbench (sass) 153 | .sass-cache/ 154 | 155 | # Installshield output folder 156 | [Ee]xpress/ 157 | 158 | # DocProject is a documentation generator add-in 159 | DocProject/buildhelp/ 160 | DocProject/Help/*.HxT 161 | DocProject/Help/*.HxC 162 | DocProject/Help/*.hhc 163 | DocProject/Help/*.hhk 164 | DocProject/Help/*.hhp 165 | DocProject/Help/Html2 166 | DocProject/Help/html 167 | 168 | # Click-Once directory 169 | publish/ 170 | 171 | # Publish Web Output 172 | *.[Pp]ublish.xml 173 | *.azurePubxml 174 | # Note: Comment the next line if you want to checkin your web deploy settings, 175 | # but database connection strings (with potential passwords) will be unencrypted 176 | *.pubxml 177 | *.publishproj 178 | 179 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 180 | # checkin your Azure Web App publish settings, but sensitive information contained 181 | # in these scripts will be unencrypted 182 | PublishScripts/ 183 | 184 | # NuGet Packages 185 | *.nupkg 186 | # The packages folder can be ignored because of Package Restore 187 | **/[Pp]ackages/* 188 | # except build/, which is used as an MSBuild target. 189 | !**/[Pp]ackages/build/ 190 | # Uncomment if necessary however generally it will be regenerated when needed 191 | #!**/[Pp]ackages/repositories.config 192 | # NuGet v3's project.json files produces more ignorable files 193 | *.nuget.props 194 | *.nuget.targets 195 | 196 | # Microsoft Azure Build Output 197 | csx/ 198 | *.build.csdef 199 | 200 | # Microsoft Azure Emulator 201 | ecf/ 202 | rcf/ 203 | 204 | # Windows Store app package directories and files 205 | AppPackages/ 206 | BundleArtifacts/ 207 | Package.StoreAssociation.xml 208 | _pkginfo.txt 209 | *.appx 210 | 211 | # Visual Studio cache files 212 | # files ending in .cache can be ignored 213 | *.[Cc]ache 214 | # but keep track of directories ending in .cache 215 | !*.[Cc]ache/ 216 | 217 | # Others 218 | ClientBin/ 219 | ~$* 220 | *~ 221 | *.dbmdl 222 | *.dbproj.schemaview 223 | *.jfm 224 | *.pfx 225 | *.publishsettings 226 | orleans.codegen.cs 227 | 228 | # Including strong name files can present a security risk 229 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 230 | #*.snk 231 | 232 | # Since there are multiple workflows, uncomment next line to ignore bower_components 233 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 234 | #bower_components/ 235 | 236 | # RIA/Silverlight projects 237 | Generated_Code/ 238 | 239 | # Backup & report files from converting an old project file 240 | # to a newer Visual Studio version. Backup files are not needed, 241 | # because we have git ;-) 242 | _UpgradeReport_Files/ 243 | Backup*/ 244 | UpgradeLog*.XML 245 | UpgradeLog*.htm 246 | ServiceFabricBackup/ 247 | *.rptproj.bak 248 | 249 | # SQL Server files 250 | *.mdf 251 | *.ldf 252 | *.ndf 253 | 254 | # Business Intelligence projects 255 | *.rdl.data 256 | *.bim.layout 257 | *.bim_*.settings 258 | *.rptproj.rsuser 259 | 260 | # Microsoft Fakes 261 | FakesAssemblies/ 262 | 263 | # GhostDoc plugin setting file 264 | *.GhostDoc.xml 265 | 266 | # Node.js Tools for Visual Studio 267 | .ntvs_analysis.dat 268 | node_modules/ 269 | 270 | # Visual Studio 6 build log 271 | *.plg 272 | 273 | # Visual Studio 6 workspace options file 274 | *.opt 275 | 276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 277 | *.vbw 278 | 279 | # Visual Studio LightSwitch build output 280 | **/*.HTMLClient/GeneratedArtifacts 281 | **/*.DesktopClient/GeneratedArtifacts 282 | **/*.DesktopClient/ModelManifest.xml 283 | **/*.Server/GeneratedArtifacts 284 | **/*.Server/ModelManifest.xml 285 | _Pvt_Extensions 286 | 287 | # Paket dependency manager 288 | .paket/paket.exe 289 | paket-files/ 290 | 291 | # FAKE - F# Make 292 | .fake/ 293 | 294 | # JetBrains Rider 295 | .idea/ 296 | *.sln.iml 297 | 298 | # CodeRush 299 | .cr/ 300 | 301 | # Python Tools for Visual Studio (PTVS) 302 | __pycache__/ 303 | *.pyc 304 | 305 | # Cake - Uncomment if you are using it 306 | # tools/** 307 | # !tools/packages.config 308 | 309 | # Tabs Studio 310 | *.tss 311 | 312 | # Telerik's JustMock configuration file 313 | *.jmconfig 314 | 315 | # BizTalk build output 316 | *.btp.cs 317 | *.btm.cs 318 | *.odx.cs 319 | *.xsd.cs 320 | 321 | # OpenCover UI analysis results 322 | OpenCover/ 323 | 324 | # Azure Stream Analytics local run output 325 | ASALocalRun/ 326 | 327 | # MSBuild Binary and Structured Log 328 | *.binlog 329 | 330 | # NVidia Nsight GPU debugger configuration file 331 | *.nvuser 332 | 333 | # MFractors (Xamarin productivity tool) working folder 334 | .mfractor/ 335 | --------------------------------------------------------------------------------