├── 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 |
--------------------------------------------------------------------------------