Skip to content

Commit

Permalink
#44 Various updates to the Copy and With methods:
Browse files Browse the repository at this point in the history
Renamed to TryWith and TryCopy
With (version that throws exceptions rather than using an Option<T> type) added
Various code changes to support edge-case badly behaved types
  • Loading branch information
DavidArno committed Dec 5, 2017
1 parent 7039868 commit 03d4cb7
Show file tree
Hide file tree
Showing 8 changed files with 604 additions and 130 deletions.
18 changes: 18 additions & 0 deletions src/SuccincT/Functional/CachedConstructorInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace SuccincT.Functional
{
internal class CachedConstructorInfo
{
public ConstructorInfo Constructor { get; }
public List<ParameterInfo> Parameters { get; }

public CachedConstructorInfo(ConstructorInfo constructorInfo)
{
Constructor = constructorInfo;
Parameters = constructorInfo.GetParameters().ToList();
}
}
}
30 changes: 30 additions & 0 deletions src/SuccincT/Functional/CachedTypeInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace SuccincT.Functional
{
internal class CachedTypeInfo
{
public List<CachedConstructorInfo> CachedPublicConstructors { get; }
public List<PropertyInfo> Properties { get; }
public List<PropertyInfo> ReadOnlyProperties { get; }
public List<PropertyInfo> WriteOnlyProperties { get; }

public CachedTypeInfo(Type type)
{
var typeInfo = type.GetTypeInfo();

var cachedConstructors =
typeInfo.DeclaredConstructors.Select(c => new CachedConstructorInfo(c)).ToList();

CachedPublicConstructors = cachedConstructors
.Where(cc => cc.Constructor.IsPublic && !cc.Constructor.IsStatic).ToList();

Properties = type.GetRuntimeProperties().ToList();
ReadOnlyProperties = Properties.Where(p => p.CanRead && !p.CanWrite).ToList();
WriteOnlyProperties = Properties.Where(p => !p.CanRead && p.CanWrite).ToList();
}
}
}
11 changes: 11 additions & 0 deletions src/SuccincT/Functional/CopyException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace SuccincT.Functional
{
public class CopyException : Exception
{
public CopyException(string message) : base(message) {}

public CopyException(string message, Exception innerException) : base(message, innerException) {}
}
}
299 changes: 191 additions & 108 deletions src/SuccincT/Functional/WithExtensions.cs

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/SuccincT/SuccincT.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Runtime" Version="4.3.0" />
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
using NUnit.Framework;
using SuccincT.Functional;
using static NUnit.Framework.Assert;

namespace SuccincTTests.SuccincT.Functional
{
[TestFixture]
public class WithExtensionsAdverseConditionsTests
{
[Test]
public void TryWithOfTypeWithConstructorAndNoGetters_FailsCleanly()
{
var x = new TypeWithConstructorAndNoGetters(1);
var y = x.TryWith(new { });

IsFalse(y.HasValue);
}

[Test]
public void WithOfTypeWithConstructorAndNoGetters_ThrowsCopyException()
{
var x = new TypeWithConstructorAndNoGetters(1);
Throws<CopyException>(() => x.With(new { }));
}

[Test]
public void TryWithOfTypeWithConstructorAndNoGettersSuppliedWithWrongParam_FailsCleanly()
{
var x = new TypeWithConstructorAndNoGetters(1);
var y = x.TryWith(new { x = "2" });

IsFalse(y.HasValue);
}

[Test]
public void WithOfTypeWithConstructorAndNoGettersSuppliedWithWrongParam_ThrowsCopyException()
{
var x = new TypeWithConstructorAndNoGetters(1);
Throws<CopyException>(() => x.With(new { x = "2" }));
}

[Test]
public void TryWithOfTypeWithConstructorAndNoGettersSuppliedWithWrongParamName_FailsCleanly()
{
var x = new TypeWithConstructorAndNoGetters(1);
var y = x.TryWith(new { y = 1 });

IsFalse(y.HasValue);
}

[Test]
public void WithOfTypeWithConstructorAndNoGettersSuppliedWithWrongParamName_ThrowsCopyException()
{
var x = new TypeWithConstructorAndNoGetters(1);
Throws<CopyException>(() => x.With(new { y = 1 }));
}

[Test]
public void TryWithOfTypeWithConstructorAndNoMatchingGetter_CanHavePropSetViaConstructor()
{
var x = new TypeWithConstructorAndNoMatchingGetter(1);
var y = x.TryWith(new { a = 2 });

AreEqual(2, y.Value.B);
}

[Test]
public void WithOfTypeWithConstructorAndNoMatchingGetter_CanHavePropSetViaConstructor()
{
var x = new TypeWithConstructorAndNoMatchingGetter(1);
var y = x.With(new { a = 2 });

AreEqual(2, y.B);
}

[Test]
public void TryWithOfTypeWithConstructorAndNoMatchingGetter_FailsCleanlyWhenPropertyNameRatherThanParamNameIsSupplied()
{
var x = new TypeWithConstructorAndNoMatchingGetter(1);
var y = x.TryWith(new { b = 2 });

IsFalse(y.HasValue);
}

[Test]
public void WithOfTypeWithConstructorAndNoMatchingGetter_ThrowsCopyExceptionWhenPropertyNameRatherThanParamNameIsSupplied()
{
var x = new TypeWithConstructorAndNoMatchingGetter(1);
Throws<CopyException>(() => x.With(new { b = 2 }));
}

[Test]
public void TryWithOfTypeWithMultipleConstructors_ChoosesCorrectConstructor()
{
var x = new TypeWithMultipleConstructors(1);

var y = x.TryWith(new { B = 3 });

AreEqual(1, y.Value.A);
AreEqual(3, y.Value.B);
}

[Test]
public void WithOfTypeWithMultipleConstructors_ChoosesCorrectConstructor()
{
var x = new TypeWithMultipleConstructors(1);

var y = x.With(new { B = 3 });

AreEqual(1, y.A);
AreEqual(3, y.B);
}

[Test]
public void TryWithOfTypeToTestPropertiesSetViaConstructorArentSetAfterConstruction_OnlySetsPropertyViaConstructor()
{
var x = new TypeToTestPropertiesSetViaConstructorArentSetAfterConstruction(2);

var y = x.TryWith(new { a = 3 });

AreEqual(3, y.Value.A);
}

[Test]
public void WithOfTypeToTestPropertiesSetViaConstructorArentSetAfterConstruction_OnlySetsPropertyViaConstructor()
{
var x = new TypeToTestPropertiesSetViaConstructorArentSetAfterConstruction(2);

var y = x.With(new { a = 3 });

AreEqual(3, y.A);
}

[Test]
public void TryWithOfTypeToTestPropertiesAreOnlySetOnceWhenNoConstructor_OnlySetsPropertyOnce()
{
var x = new TypeToTestPropertiesAreOnlySetOnceWhenNoConstructor { A = 2 };

var y = x.TryWith(new { a = 3 });

AreEqual(3, y.Value.A);
}

[Test]
public void WithOfTypeToTestPropertiesAreOnlySetOnceWhenNoConstructor_OnlySetsPropertyOnce()
{
var x = new TypeToTestPropertiesAreOnlySetOnceWhenNoConstructor { A = 2 };

var y = x.With(new { a = 3 });

AreEqual(3, y.A);
}

private class TypeWithConstructorAndNoGetters
{
// ReSharper disable once UnusedParameter.Local
public TypeWithConstructorAndNoGetters(int x) { }
}

private class TypeWithConstructorAndNoMatchingGetter
{
// ReSharper disable once UnusedMember.Local
// ReSharper disable UnusedParameter.Local
public TypeWithConstructorAndNoMatchingGetter(int a, int c) { }
public TypeWithConstructorAndNoMatchingGetter(int a) => B = a;
public int B { get; }
}

private class TypeWithMultipleConstructors
{
public TypeWithMultipleConstructors(int a) => (A, B) = (a, 1);
// ReSharper disable once UnusedMember.Local
public TypeWithMultipleConstructors(int a, int b) => (A, B) = (a, b);
// ReSharper disable once UnusedMember.Local
// ReSharper disable once UnusedParameter.Local
public TypeWithMultipleConstructors(int a, int c, int b) => (A, B) = (a, b);
public int A { get; }
public int B { get; }
}

private class TypeToTestPropertiesSetViaConstructorArentSetAfterConstruction
{
private int _a;

public TypeToTestPropertiesSetViaConstructorArentSetAfterConstruction(int a) => A = a;

public int A
{
get => _a;
// ReSharper disable once MemberCanBePrivate.Local
set => _a = _a + value;
}
}

private class TypeToTestPropertiesAreOnlySetOnceWhenNoConstructor
{
private int _a;

public int A
{
get => _a;
set => _a = _a + value;
}
}
}
}
Loading

0 comments on commit 03d4cb7

Please sign in to comment.