| | 1 | | // Copyright (c) Microsoft Corporation. All rights reserved. |
| | 2 | | // Licensed under the MIT License. See License.txt in the project root for |
| | 3 | | // license information. |
| | 4 | |
|
| | 5 | | using Microsoft.Azure.Search.Tests.Utilities; |
| | 6 | | using System; |
| | 7 | | using System.Collections; |
| | 8 | | using System.Collections.Generic; |
| | 9 | | using System.Linq; |
| | 10 | | using Xunit; |
| | 11 | |
|
| | 12 | | namespace Microsoft.Azure.Search.Tests |
| | 13 | | { |
| | 14 | | public sealed class ModelComparerTests |
| | 15 | | { |
| | 16 | | private enum Direction |
| | 17 | | { |
| | 18 | | Right, |
| | 19 | | Left |
| | 20 | | } |
| | 21 | |
|
| | 22 | | [Fact] |
| | 23 | | public void CanComparePrimitives() |
| | 24 | | { |
| 0 | 25 | | Assert.Equal(5, 5, new ModelComparer<int>()); |
| 0 | 26 | | Assert.Equal(3.14, 3.14, new ModelComparer<double>()); |
| 0 | 27 | | Assert.Equal('c', 'c', new ModelComparer<char>()); |
| 0 | 28 | | Assert.Equal(5, 5L, new ModelComparer<object>()); |
| | 29 | |
|
| 0 | 30 | | Assert.NotEqual(5, 1, new ModelComparer<int>()); |
| 0 | 31 | | Assert.NotEqual(3.14, -2.7, new ModelComparer<double>()); |
| 0 | 32 | | Assert.NotEqual('c', 'z', new ModelComparer<char>()); |
| 0 | 33 | | Assert.NotEqual(3, 3.0, new ModelComparer<object>()); |
| 0 | 34 | | } |
| | 35 | |
|
| | 36 | | [Fact] |
| | 37 | | public void CanCompareEnums() |
| | 38 | | { |
| 0 | 39 | | Assert.Equal(Direction.Left, Direction.Left, new ModelComparer<Direction>()); |
| 0 | 40 | | Assert.NotEqual(Direction.Left, Direction.Right, new ModelComparer<Direction>()); |
| 0 | 41 | | } |
| | 42 | |
|
| | 43 | | [Fact] |
| | 44 | | public void CanCompareNulls() |
| | 45 | | { |
| 0 | 46 | | Model nullModel = null; |
| | 47 | |
|
| 0 | 48 | | Assert.Equal(nullModel, nullModel, new ModelComparer<Model>()); |
| 0 | 49 | | Assert.NotEqual(new Model(), nullModel, new ModelComparer<Model>()); |
| 0 | 50 | | Assert.NotEqual(nullModel, new Model(), new ModelComparer<Model>()); |
| | 51 | |
|
| 0 | 52 | | IEnumerable<int> nullCollection = null; |
| | 53 | |
|
| 0 | 54 | | Assert.True(new ModelComparer<IEnumerable<int>>().Equals(nullCollection, nullCollection)); |
| 0 | 55 | | Assert.False(new ModelComparer<IEnumerable<int>>().Equals(new[] { 1 }, nullCollection)); |
| 0 | 56 | | Assert.False(new ModelComparer<IEnumerable<int>>().Equals(nullCollection, new[] { 4, 5 })); |
| 0 | 57 | | } |
| | 58 | |
|
| | 59 | | [Fact] |
| | 60 | | public void CanCompareExtensibleEnums() |
| | 61 | | { |
| 0 | 62 | | Assert.Equal(FancyDirection.Left, FancyDirection.Left, new ModelComparer<FancyDirection>()); |
| 0 | 63 | | Assert.NotEqual(FancyDirection.Left, FancyDirection.Right, new ModelComparer<FancyDirection>()); |
| 0 | 64 | | } |
| | 65 | |
|
| | 66 | | [Fact] |
| | 67 | | public void CanCompareNullables() |
| | 68 | | { |
| 0 | 69 | | int? maybeSeven = 7; |
| 0 | 70 | | int? maybeFive = 5; |
| 0 | 71 | | double? maybePi = 3.14; |
| 0 | 72 | | double? maybeE = 2.718; |
| 0 | 73 | | bool? maybeTrue = true; |
| 0 | 74 | | bool? maybeFalse = false; |
| | 75 | |
|
| 0 | 76 | | Assert.Equal(maybeSeven, maybeSeven, new ModelComparer<int?>()); |
| 0 | 77 | | Assert.Equal(maybePi, maybePi, new ModelComparer<double?>()); |
| 0 | 78 | | Assert.Equal(maybeTrue, maybeTrue, new ModelComparer<bool?>()); |
| | 79 | |
|
| 0 | 80 | | Assert.NotEqual(maybeSeven, maybeFive, new ModelComparer<int?>()); |
| 0 | 81 | | Assert.NotEqual(maybePi, maybeE, new ModelComparer<double?>()); |
| 0 | 82 | | Assert.NotEqual(maybeTrue, maybeFalse, new ModelComparer<bool?>()); |
| | 83 | |
|
| | 84 | | #pragma warning disable xUnit2003 // Do not use equality check to test for null value |
| 0 | 85 | | Assert.NotEqual(null, maybeSeven, new ModelComparer<int?>()); |
| 0 | 86 | | Assert.NotEqual(null, maybePi, new ModelComparer<double?>()); |
| 0 | 87 | | Assert.NotEqual(null, maybeTrue, new ModelComparer<bool?>()); |
| | 88 | | #pragma warning restore xUnit2003 // Do not use equality check to test for null value |
| | 89 | |
|
| | 90 | | #pragma warning disable xUnit2000 // Literal or constant should be the first argument to Assert.NotEqual |
| 0 | 91 | | Assert.NotEqual(maybeSeven, null, new ModelComparer<int?>()); |
| 0 | 92 | | Assert.NotEqual(maybePi, null, new ModelComparer<double?>()); |
| 0 | 93 | | Assert.NotEqual(maybeTrue, null, new ModelComparer<bool?>()); |
| | 94 | | #pragma warning restore xUnit2000 // Literal or constant should be the first argument to Assert.NotEqual |
| 0 | 95 | | } |
| | 96 | |
|
| | 97 | | [Fact] |
| | 98 | | public void CanCompareCollections() |
| | 99 | | { |
| 0 | 100 | | var ints = new[] { 1, 2, 3 }; |
| 0 | 101 | | var otherInts = new[] { 4, 5, 6 }; |
| 0 | 102 | | var longerInts = new[] { 1, 2, 3, 4 }; |
| 0 | 103 | | var strings = new[] { "hello", "bonjour", "guten tag", "你好", "aloha" }; |
| 0 | 104 | | var otherStrings = new[] { "good bye", "au revoir", "auf wiedersehen", "再見", "aloha" }; |
| | 105 | |
|
| 0 | 106 | | var intComparer = new ModelComparer<int[]>(); |
| 0 | 107 | | var stringComparer = new ModelComparer<string[]>(); |
| | 108 | |
|
| | 109 | | // We have to use Assert.True/False instead of .Equal/.NotEqual or we won't actually exercise the ModelCompa |
| | 110 | | // Also, we use deep copies for some comparisons to make sure the ModelComparer isn't falling back on refere |
| 0 | 111 | | Assert.True(intComparer.Equals(ints, ints)); |
| 0 | 112 | | Assert.True(stringComparer.Equals(strings, strings)); |
| | 113 | |
|
| 0 | 114 | | Assert.True(intComparer.Equals(ints, (int[])ints.Clone())); |
| 0 | 115 | | Assert.True(stringComparer.Equals((string[])strings.Clone(), strings)); |
| | 116 | |
|
| 0 | 117 | | Assert.False(intComparer.Equals(ints, otherInts)); |
| 0 | 118 | | Assert.False(stringComparer.Equals(otherStrings, strings)); |
| | 119 | |
|
| 0 | 120 | | Assert.False(intComparer.Equals(ints, new[] { 7 })); |
| 0 | 121 | | Assert.False(stringComparer.Equals(new[] { "who?" }, strings)); |
| | 122 | |
|
| | 123 | | // Test collections of varying lengths. |
| 0 | 124 | | Assert.False(intComparer.Equals(ints, longerInts)); |
| 0 | 125 | | Assert.False(intComparer.Equals(longerInts, ints)); |
| 0 | 126 | | } |
| | 127 | |
|
| | 128 | | [Fact] |
| | 129 | | public void CanCompareDictionaries() |
| | 130 | | { |
| 0 | 131 | | var x = new Dictionary<string, double>() { { "pi", 3.14 }, { "e", 2.718 } }; |
| 0 | 132 | | var notX = new Dictionary<string, double>() { { "m", 13.3 }, { "v", 77 } }; |
| | 133 | |
|
| 0 | 134 | | var y = new Dictionary<string, object>() { { "a", "test" }, { "b", "test2" } }; |
| 0 | 135 | | var notY = new Dictionary<string, object>() { { "w", "other" }, { "u", "side" } }; |
| | 136 | |
|
| 0 | 137 | | var doubleComparer = new ModelComparer<Dictionary<string, double>>(); |
| 0 | 138 | | var stringComparer = new ModelComparer<Dictionary<string, object>>(); |
| | 139 | |
|
| | 140 | | // We have to use Assert.True/False instead of .Equal/.NotEqual or we won't actually exercise the ModelCompa |
| | 141 | | // Also, we use deep copies for some comparisons to make sure the ModelComparer isn't falling back on refere |
| 0 | 142 | | Assert.True(doubleComparer.Equals(x, x)); |
| 0 | 143 | | Assert.True(stringComparer.Equals(y, y)); |
| | 144 | |
|
| 0 | 145 | | Assert.True(doubleComparer.Equals(x, new Dictionary<string, double>(x))); |
| 0 | 146 | | Assert.True(stringComparer.Equals(new Dictionary<string, object>(y), y)); |
| | 147 | |
|
| 0 | 148 | | Assert.False(doubleComparer.Equals(x, notX)); |
| 0 | 149 | | Assert.False(stringComparer.Equals(notY, y)); |
| | 150 | |
|
| 0 | 151 | | Assert.False(doubleComparer.Equals(x, new Dictionary<string, double>() { { "r", 12.0 } })); |
| 0 | 152 | | Assert.False(stringComparer.Equals(new Dictionary<string, object>() { { "c", "other" } }, y)); |
| 0 | 153 | | } |
| | 154 | |
|
| | 155 | | [Fact] |
| | 156 | | public void IEquatableTakesPrecedenceOverIEnumerable() |
| | 157 | | { |
| 0 | 158 | | var ints = new BiasedList(1, 2, 3); |
| 0 | 159 | | var odds = new BiasedList(1, 3, 5); |
| 0 | 160 | | var evens = new BiasedList(2, 4, 6); |
| | 161 | |
|
| 0 | 162 | | var comparer = new ModelComparer<BiasedList>(); |
| | 163 | |
|
| 0 | 164 | | Assert.True(comparer.Equals(ints, ints)); |
| 0 | 165 | | Assert.True(comparer.Equals(ints, new BiasedList(ints))); |
| | 166 | |
|
| | 167 | | // ints and odds should compare as equal because BiasedList only looks at the first element. |
| 0 | 168 | | Assert.True(comparer.Equals(odds, ints)); |
| 0 | 169 | | Assert.True(comparer.Equals(new BiasedList(odds), ints)); |
| | 170 | |
|
| 0 | 171 | | Assert.False(comparer.Equals(ints, evens)); |
| 0 | 172 | | Assert.False(comparer.Equals(ints, new BiasedList(evens))); |
| 0 | 173 | | } |
| | 174 | |
|
| | 175 | | [Fact] |
| | 176 | | public void CanComparePolymorphicObjects() |
| | 177 | | { |
| 0 | 178 | | var cat = new Cat("Gizmo"); |
| 0 | 179 | | var otherCat = new Cat("Ness"); |
| 0 | 180 | | var dog = new Dog("Mara"); |
| 0 | 181 | | var otherDog = new Dog("Daisy"); |
| | 182 | |
|
| 0 | 183 | | var comparer = new ModelComparer<Animal>(); |
| | 184 | |
|
| 0 | 185 | | Assert.Equal(dog, dog, comparer); |
| 0 | 186 | | Assert.Equal(dog, new Dog(dog), comparer); |
| 0 | 187 | | Assert.Equal(cat, cat, comparer); |
| 0 | 188 | | Assert.Equal(cat, new Cat(cat), comparer); |
| | 189 | |
|
| 0 | 190 | | Assert.NotEqual(dog, otherDog, comparer); |
| 0 | 191 | | Assert.NotEqual(cat, otherCat, comparer); |
| 0 | 192 | | Assert.NotEqual(dog, cat, comparer); |
| 0 | 193 | | } |
| | 194 | |
|
| | 195 | | [Fact] |
| | 196 | | public void CanCompareDtos() |
| | 197 | | { |
| 0 | 198 | | var model = new Model() { Name = "Magical Trevor", Age = 11 }; |
| 0 | 199 | | var sameModel = new Model(model); |
| 0 | 200 | | var differentModel = new Model() { Name = "Mr. Stabby", Age = 35 }; |
| | 201 | |
|
| 0 | 202 | | var comparer = new ModelComparer<Model>(); |
| | 203 | |
|
| 0 | 204 | | Assert.Equal(model, model, comparer); |
| 0 | 205 | | Assert.Equal(model, sameModel, comparer); |
| 0 | 206 | | Assert.NotEqual(model, differentModel, comparer); |
| 0 | 207 | | } |
| | 208 | |
|
| | 209 | | [Fact] |
| | 210 | | public void ComparingMarkerClassesAlwaysReturnsTrue() |
| | 211 | | { |
| 0 | 212 | | var marker = new Empty(); |
| 0 | 213 | | var otherMarker = new Empty(); |
| | 214 | |
|
| 0 | 215 | | Assert.Equal(marker, marker, new ModelComparer<Empty>()); |
| 0 | 216 | | Assert.Equal(marker, otherMarker, new ModelComparer<Empty>()); |
| 0 | 217 | | } |
| | 218 | |
|
| | 219 | | [Fact] |
| | 220 | | public void CanCompareDifferentEnumerableTypes() |
| | 221 | | { |
| 0 | 222 | | var intArray = new[] { 1, 2, 3 }; |
| 0 | 223 | | var intList = new List<int>() { 1, 2, 3 }; |
| | 224 | |
|
| 0 | 225 | | var intComparer = new ModelComparer<IEnumerable<int>>(); |
| | 226 | |
|
| | 227 | | // We have to use Assert.True/False instead of .Equal/.NotEqual or we won't actually exercise the ModelCompa |
| 0 | 228 | | Assert.True(intComparer.Equals(intArray, intList)); |
| 0 | 229 | | } |
| | 230 | |
|
| | 231 | | [Fact] |
| | 232 | | public void IntegerComparisonsUseWidestType() |
| | 233 | | { |
| 0 | 234 | | byte b = 10; |
| 0 | 235 | | short s = 10; |
| 0 | 236 | | int i = 10; |
| 0 | 237 | | long l = 10; |
| | 238 | |
|
| 0 | 239 | | var comparer = new ModelComparer<object>(); |
| | 240 | |
|
| 0 | 241 | | Assert.Equal(b, s, comparer); |
| 0 | 242 | | Assert.Equal(b, i, comparer); |
| 0 | 243 | | Assert.Equal(b, l, comparer); |
| 0 | 244 | | Assert.Equal(s, b, comparer); |
| 0 | 245 | | Assert.Equal(s, i, comparer); |
| 0 | 246 | | Assert.Equal(s, l, comparer); |
| 0 | 247 | | Assert.Equal(i, b, comparer); |
| 0 | 248 | | Assert.Equal(i, s, comparer); |
| 0 | 249 | | Assert.Equal(i, l, comparer); |
| 0 | 250 | | Assert.Equal(l, b, comparer); |
| 0 | 251 | | Assert.Equal(l, s, comparer); |
| 0 | 252 | | Assert.Equal(l, i, comparer); |
| 0 | 253 | | } |
| | 254 | |
|
| | 255 | | [Fact] |
| | 256 | | public void CanCompareComplexModels() |
| | 257 | | { |
| 0 | 258 | | var homeAddress = new USAddress() |
| 0 | 259 | | { |
| 0 | 260 | | Street = "308 Negra Arroyo Lane", |
| 0 | 261 | | City = "Albuquerque", |
| 0 | 262 | | State = "New Mexico", |
| 0 | 263 | | ZipCode = 87104 |
| 0 | 264 | | }; |
| | 265 | |
|
| 0 | 266 | | var alternateAddress = new USAddress() |
| 0 | 267 | | { |
| 0 | 268 | | Street = "Los Pollos Hermanos, 123 Central Ave Southeast", |
| 0 | 269 | | City = "Albuquerque", |
| 0 | 270 | | State = "New Mexico", |
| 0 | 271 | | ZipCode = 87108 |
| 0 | 272 | | }; |
| | 273 | |
|
| 0 | 274 | | var theOneWhoKnocks = new Customer() |
| 0 | 275 | | { |
| 0 | 276 | | FirstName = "Walter", |
| 0 | 277 | | LastName = "White", |
| 0 | 278 | | Aliases = new[] { "Heisenberg", "The One Who Knocks" }, |
| 0 | 279 | | Age = 51, |
| 0 | 280 | | Address = homeAddress, |
| 0 | 281 | | Directions = new List<Direction?>() { null, Direction.Left, null, Direction.Right }, |
| 0 | 282 | | Occupations = new Dictionary<string, Address>() |
| 0 | 283 | | { |
| 0 | 284 | | { "Husband and father", homeAddress }, |
| 0 | 285 | | { "Career criminal", alternateAddress } |
| 0 | 286 | | } |
| 0 | 287 | | }; |
| | 288 | |
|
| 0 | 289 | | var walterWhite = theOneWhoKnocks.Clone(); |
| | 290 | |
|
| 0 | 291 | | var gustavoFring = new Customer() |
| 0 | 292 | | { |
| 0 | 293 | | FirstName = "Gustavo", |
| 0 | 294 | | LastName = "Fring", |
| 0 | 295 | | Age = 49, |
| 0 | 296 | | Address = alternateAddress |
| 0 | 297 | | }; |
| | 298 | |
|
| 0 | 299 | | var comparer = new ModelComparer<Customer>(); |
| | 300 | |
|
| 0 | 301 | | Assert.Equal(theOneWhoKnocks, theOneWhoKnocks, comparer); |
| 0 | 302 | | Assert.Equal(walterWhite, theOneWhoKnocks, comparer); |
| 0 | 303 | | Assert.NotEqual(gustavoFring, theOneWhoKnocks, comparer); |
| 0 | 304 | | } |
| | 305 | |
|
| | 306 | | private class Empty { } |
| | 307 | |
|
| | 308 | | private class Model |
| | 309 | | { |
| 0 | 310 | | public Model() { } |
| | 311 | |
|
| 0 | 312 | | public Model(Model other) |
| | 313 | | { |
| 0 | 314 | | Name = other.Name; |
| 0 | 315 | | Age = other.Age; |
| 0 | 316 | | } |
| | 317 | |
|
| 0 | 318 | | public string Name { get; set; } |
| | 319 | |
|
| 0 | 320 | | public int Age { get; set; } |
| | 321 | | } |
| | 322 | |
|
| | 323 | | private struct FancyDirection : IEquatable<FancyDirection> |
| | 324 | | { |
| | 325 | | private readonly string _value; |
| | 326 | |
|
| 0 | 327 | | public static readonly FancyDirection Left = new FancyDirection("left"); |
| | 328 | |
|
| 0 | 329 | | public static readonly FancyDirection Right = new FancyDirection("right"); |
| | 330 | |
|
| | 331 | | private FancyDirection(string name) |
| | 332 | | { |
| 0 | 333 | | _value = name ?? throw new ArgumentNullException(nameof(name)); |
| 0 | 334 | | } |
| | 335 | |
|
| 0 | 336 | | public static bool operator ==(FancyDirection lhs, FancyDirection rhs) => Equals(lhs, rhs); |
| | 337 | |
|
| 0 | 338 | | public static bool operator !=(FancyDirection lhs, FancyDirection rhs) => !Equals(lhs, rhs); |
| | 339 | |
|
| 0 | 340 | | public bool Equals(FancyDirection other) => _value == other._value; |
| | 341 | |
|
| 0 | 342 | | public override bool Equals(object obj) => obj is FancyDirection ? Equals((FancyDirection)obj) : false; |
| | 343 | |
|
| 0 | 344 | | public override int GetHashCode() => _value.GetHashCode(); |
| | 345 | |
|
| 0 | 346 | | public override string ToString() => _value; |
| | 347 | | } |
| | 348 | |
|
| | 349 | | // Biased lists only care about the first element when it comes to equality comparison. |
| | 350 | | private class BiasedList : IEnumerable<int>, IEquatable<BiasedList> |
| | 351 | | { |
| | 352 | | private readonly int[] _values; |
| | 353 | |
|
| 0 | 354 | | public BiasedList(params int[] values) |
| | 355 | | { |
| 0 | 356 | | if (values == null) |
| | 357 | | { |
| 0 | 358 | | throw new ArgumentNullException(nameof(values)); |
| | 359 | | } |
| | 360 | |
|
| 0 | 361 | | if (values.Length == 0) |
| | 362 | | { |
| 0 | 363 | | throw new ArgumentOutOfRangeException(nameof(values), "List must be non-empty"); |
| | 364 | | } |
| | 365 | |
|
| 0 | 366 | | _values = values; |
| 0 | 367 | | } |
| | 368 | |
|
| 0 | 369 | | public BiasedList(BiasedList other) |
| | 370 | | { |
| 0 | 371 | | if (other == null) |
| | 372 | | { |
| 0 | 373 | | throw new ArgumentNullException(nameof(other)); |
| | 374 | | } |
| | 375 | |
|
| 0 | 376 | | _values = (int[])other._values.Clone(); |
| 0 | 377 | | } |
| | 378 | |
|
| 0 | 379 | | public bool Equals(BiasedList other) => other != null && this.First() == other.First(); |
| | 380 | |
|
| 0 | 381 | | public IEnumerator<int> GetEnumerator() => ((IEnumerable<int>)_values).GetEnumerator(); |
| | 382 | |
|
| 0 | 383 | | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); |
| | 384 | | } |
| | 385 | |
|
| | 386 | | private abstract class Animal |
| | 387 | | { |
| 0 | 388 | | protected Animal(string name) |
| | 389 | | { |
| 0 | 390 | | Name = name; |
| 0 | 391 | | } |
| | 392 | |
|
| | 393 | | public abstract string Noise { get; } |
| | 394 | |
|
| 0 | 395 | | public string Name { get; } |
| | 396 | | } |
| | 397 | |
|
| | 398 | | private sealed class Dog : Animal, IEquatable<Dog> |
| | 399 | | { |
| | 400 | | private readonly Random _random; |
| | 401 | |
|
| 0 | 402 | | public Dog(string name) : base(name) |
| | 403 | | { |
| 0 | 404 | | _random = new Random(); |
| 0 | 405 | | } |
| | 406 | |
|
| 0 | 407 | | public Dog(Dog dog) : this(dog.Name) { } |
| | 408 | |
|
| | 409 | | // Add some randomness to ensure that IEquatable always takes precedence. |
| 0 | 410 | | public override string Noise => _random.Next() % 2 == 0 ? "woof!" : "bark!"; |
| | 411 | |
|
| 0 | 412 | | public bool Equals(Dog other) => other != null && this.Name == other.Name; |
| | 413 | | } |
| | 414 | |
|
| | 415 | | private sealed class Cat : Animal, IEquatable<Cat> |
| | 416 | | { |
| 0 | 417 | | public Cat(string name) : base(name) { } |
| | 418 | |
|
| 0 | 419 | | public Cat(Cat cat) : this(cat.Name) { } |
| | 420 | |
|
| 0 | 421 | | public override string Noise => "meow!"; |
| | 422 | |
|
| 0 | 423 | | public bool Equals(Cat other) => other != null && this.Name == other.Name; |
| | 424 | | } |
| | 425 | |
|
| | 426 | | private class Customer |
| | 427 | | { |
| 0 | 428 | | public Customer() { } |
| | 429 | |
|
| 0 | 430 | | public Customer(Customer other) |
| | 431 | | { |
| 0 | 432 | | Aliases = other.Aliases.ToArray(); |
| 0 | 433 | | Age = other.Age; |
| 0 | 434 | | Address = other.Address.Clone(); |
| 0 | 435 | | FirstName = other.FirstName; |
| 0 | 436 | | LastName = other.LastName; |
| 0 | 437 | | Directions = other.Directions.ToList(); |
| 0 | 438 | | Occupations = other.Occupations.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Clone()); |
| 0 | 439 | | } |
| | 440 | |
|
| 0 | 441 | | public string Name => FirstName + " " + LastName; |
| | 442 | |
|
| 0 | 443 | | public IList<string> Aliases { get; set; } |
| | 444 | |
|
| 0 | 445 | | public int? Age { get; set; } |
| | 446 | |
|
| 0 | 447 | | public Address Address { get; set; } |
| | 448 | |
|
| 0 | 449 | | public string FirstName { get; set; } |
| | 450 | |
|
| 0 | 451 | | public string LastName { get; set; } |
| | 452 | |
|
| 0 | 453 | | public List<Direction?> Directions { get; set; } |
| | 454 | |
|
| 0 | 455 | | public Dictionary<string, Address> Occupations { get; set; } |
| | 456 | |
|
| 0 | 457 | | public Customer Clone() => new Customer(this); |
| | 458 | | } |
| | 459 | |
|
| | 460 | | private abstract class Address |
| | 461 | | { |
| 0 | 462 | | public string Street { get; set; } |
| | 463 | |
|
| 0 | 464 | | public string City { get; set; } |
| | 465 | |
|
| | 466 | | public abstract Address Clone(); |
| | 467 | | } |
| | 468 | |
|
| | 469 | | private sealed class USAddress : Address |
| | 470 | | { |
| 0 | 471 | | public USAddress() { } |
| | 472 | |
|
| 0 | 473 | | public USAddress(USAddress other) |
| | 474 | | { |
| 0 | 475 | | Street = other.Street; |
| 0 | 476 | | City = other.City; |
| 0 | 477 | | State = other.State; |
| 0 | 478 | | ZipCode = other.ZipCode; |
| 0 | 479 | | } |
| | 480 | |
|
| 0 | 481 | | public string State { get; set; } |
| | 482 | |
|
| 0 | 483 | | public int ZipCode { get; set; } |
| | 484 | |
|
| 0 | 485 | | public override Address Clone() => new USAddress(this); |
| | 486 | | } |
| | 487 | | } |
| | 488 | | } |