Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove redundant abstract/virtual properties, only one is needed. #27

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
08db4fa
Add ArrayPrimitiveTypeExpressionFactory.
deipax Sep 5, 2017
867690b
Small fix.
deipax Sep 5, 2017
d2035d9
Add support for polymorphism.
deipax Sep 6, 2017
7e963d0
Revert "Add support for polymorphism."
deipax Sep 7, 2017
5984423
Supply a Expression factory for a List of primitives.
deipax Sep 7, 2017
0c7b379
Merge remote-tracking branch 'refs/remotes/upstream/master'
deipax Sep 7, 2017
b5475f7
There is no need to clone any primitive types, they can be used direc…
deipax Sep 11, 2017
fd91db2
Merge Remote
deipax Sep 11, 2017
c4fb91f
I am not sure if this is considered a bug or not, but Tuples are clas…
deipax Sep 12, 2017
efefcf4
Merge remote-tracking branch 'refs/remotes/upstream-master/master'
deipax Sep 28, 2017
af1740c
Leverage backing fields for automatic properties.
deipax Sep 28, 2017
b02b431
Revert "Leverage backing fields for automatic properties."
deipax Oct 23, 2017
2c9fda4
Merge remote-tracking branch 'refs/remotes/upstream-master/master'
deipax Oct 23, 2017
681565d
Merge remote-tracking branch 'refs/remotes/upstream-master/master'
deipax Oct 24, 2017
ed144b5
Potential fix for issue #25
deipax Oct 24, 2017
8f164a9
Add Unit tests for Abstract and Virtual properties.
deipax Oct 25, 2017
6978185
Add a "new" Field to unit test base class and sub class.
deipax Oct 25, 2017
bfcf8d6
Merge remote-tracking branch 'refs/remotes/upstream-master/master'
deipax Oct 25, 2017
3464307
Remove redundant abstract/virtual properties, only one is needed.
deipax Oct 25, 2017
b75a355
delete unneeded db.lock
deipax Oct 25, 2017
977eed9
Remove redundant abstract/virtual properties. Use extension function…
deipax Nov 1, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,4 @@ FakesAssemblies/
*.sln.ide/
BenchmarkDotNet.Artifacts/results/
.vscode/
/.vs/CloneExtensions/v15/Server/sqlite3
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.10.9" />
</ItemGroup>
<ItemGroup>
<Folder Include="BenchmarkDotNet.Artifacts\" />
</ItemGroup>
</Project>
4 changes: 1 addition & 3 deletions src/CloneExtensions.UnitTests/CollectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ public void GetClone_DerivedTypeWithShadowedProperty_ClonnedProperly()
var target = CloneFactory.GetClone(source);

Assert.AreEqual(1, target.Property);

// TODO: Make it work ...
// Assert.AreEqual(2, ((BaseClass)target).Property);
Assert.AreEqual(2, ((BaseClass)target).Property);
}

class MyClass
Expand Down
51 changes: 49 additions & 2 deletions src/CloneExtensions.UnitTests/ComplexTypeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,31 @@ public void GetClone_DerivedTypeWithShadowedProperty_ClonnedProperly()
Assert.AreEqual("test2", ((BaseClassOne)target).VirtualProperty3);
}

[TestMethod]
public void GetClone_DerivedTypeWithShadowedProperty_ClonnedProperly2()
{
D source = new D()
{
Foo = "D"
};

((C)source).Foo = "C";
((B)source).Foo = "B";
((A)source).Foo = "A";

Assert.AreEqual("C", source.Foo);
Assert.AreEqual("C", ((C)source).Foo);
Assert.AreEqual("A", ((B)source).Foo);
Assert.AreEqual("A", ((A)source).Foo);

var target = source.GetClone();

Assert.AreEqual("C", target.Foo);
Assert.AreEqual("C", ((C)target).Foo);
Assert.AreEqual("A", ((B)target).Foo);
Assert.AreEqual("A", ((A)target).Foo);
}

struct SimpleStruct
{
public int _field;
Expand Down Expand Up @@ -198,7 +223,7 @@ class CircularReference2
public CircularReference1 Other { get;set; }
}

abstract class BaseClassOne
abstract class BaseClassOne : IInterface
{
public int MyField;

Expand All @@ -212,7 +237,9 @@ virtual public string VirtualProperty3
get { return _virtualProperty; }
set { _virtualProperty = string.Empty; }
}


public int InterfaceProperty { get; set; }

private string _virtualProperty;
}

Expand All @@ -226,5 +253,25 @@ class DerivedClassOne : BaseClassOne
// use the default implementation for VirtualProperty2
public override string VirtualProperty3 { get; set; }
}

public class A
{
public virtual string Foo { get; set; }
}

public class B : A
{
public override string Foo { get; set; }
}

public class C : B
{
public virtual new string Foo { get; set; }
}

public class D : C
{
public override string Foo { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using CloneExtensions.Extensions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -83,36 +84,38 @@ private Expression GetInitializationExpression()

private Expression GetFieldsCloneExpression(Func<Type, Expression, Expression> getItemCloneExpression)
{
var fields = from f in _type.GetTypeInfo().GetFields(BindingFlags.Public | BindingFlags.Instance)
where !f.GetCustomAttributes(typeof(NonClonedAttribute), true).Any()
where !f.IsInitOnly
select new Member(f, f.FieldType);

return GetMembersCloneExpression(fields.ToArray(), getItemCloneExpression);
var fields = _type
.GetAllFields()
.Where(x =>
x.CanRead &&
x.CanWrite &&
!x.IsLiteral &&
!x.IsBackingField &&
x.IsPublic &&
x.GetCustomAttributes<NonClonedAttribute>().Count() == 0)
.Select(x => new Member(x.FieldInfo, x.FieldInfo.FieldType))
.ToArray();

return GetMembersCloneExpression(fields, getItemCloneExpression);
}

private Expression GetPropertiesCloneExpression(Func<Type, Expression, Expression> getItemCloneExpression)
{
// get all private fields with `>k_BackingField` in case we can use them instead of automatic properties
var backingFields = GetBackingFields(_type).ToDictionary(f => new BackingFieldInfo(f.DeclaringType, f.Name));

// use the backing fields if available, otherwise use property
var members = new List<Member>();
var properties = GetProperties(_type);
foreach (var property in properties)
{
FieldInfo fieldInfo;
if (backingFields.TryGetValue(new BackingFieldInfo(property.DeclaringType, "<" + property.Name + ">k__BackingField"), out fieldInfo))
{
members.Add(new Member(fieldInfo, fieldInfo.FieldType));
}
else
{
members.Add(new Member(property, property.PropertyType));
}
}

return GetMembersCloneExpression(members.ToArray(), getItemCloneExpression);
var members = _type
.GetFilteredProperties()
.Where(x =>
x.CanRead &&
x.CanWrite &&
x.IsPublic &&
!x.HasParameters &&
!x.IsLiteral &&
x.GetCustomAttributes<NonClonedAttribute>().Count() == 0)
.Select(x => x.HasBackingField ?
new Member(x.BackingField.FieldInfo, x.BackingField.FieldInfo.FieldType) :
new Member(x.PropertyInfo, x.PropertyInfo.PropertyType))
.ToArray();

return GetMembersCloneExpression(members, getItemCloneExpression);
}

private Expression GetMembersCloneExpression(Member[] members, Func<Type, Expression, Expression> getItemCloneExpression)
Expand Down Expand Up @@ -174,65 +177,5 @@ private Expression GetForeachAddExpression(Type collectionType)
)
);
}

private static IEnumerable<FieldInfo> GetBackingFields(Type type)
{
TypeInfo typeInfo = type.GetTypeInfo();

while(typeInfo != null && typeInfo.UnderlyingSystemType != _objectType)
{
foreach (var field in typeInfo.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
if (field.Name.Contains(">k__BackingField") && field.DeclaringType == typeInfo.UnderlyingSystemType)
yield return field;
}

typeInfo = typeInfo.BaseType?.GetTypeInfo();
}
}

private static IEnumerable<PropertyInfo> GetProperties(Type type)
{
TypeInfo typeInfo = type.GetTypeInfo();

while (typeInfo != null && typeInfo.UnderlyingSystemType != _objectType)
{
var properties = from p in typeInfo.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
let setMethod = p.GetSetMethod(false)
let getMethod = p.GetGetMethod(false)
where !p.GetCustomAttributes(typeof(NonClonedAttribute), true).Any()
where setMethod != null && getMethod != null && !p.GetIndexParameters().Any()
select p;

foreach (var p in properties)
{
yield return p;
}

typeInfo = typeInfo.BaseType?.GetTypeInfo();
}
}

private struct BackingFieldInfo : IEquatable<BackingFieldInfo>
{
public Type DeclaredType { get; }
public string Name { get; set; }

public BackingFieldInfo(Type declaringType, string name) : this()
{
DeclaredType = declaringType;
Name = name;
}

public bool Equals(BackingFieldInfo other)
{
return other.DeclaredType == this.DeclaredType && other.Name == this.Name;
}

public override int GetHashCode()
{
return (17 * 23 + DeclaredType.GetHashCode()) * 23 + Name.GetHashCode();
}
}
}
}
19 changes: 19 additions & 0 deletions src/CloneExtensions/Extensions/FieldInfoExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Reflection;

namespace CloneExtensions.Extensions
{
public static class FieldInfoExtensions
{
public static bool IsBackingField(
this FieldInfo source,
bool defaultValue = false)
{
if (source != null)
{
return source.Name.IndexOf(">k__BackingField", 0) >= 0;
}

return defaultValue;
}
}
}
21 changes: 21 additions & 0 deletions src/CloneExtensions/Extensions/IModelInfoExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using CloneExtensions.Interfaces;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace CloneExtensions.Extensions
{
public static class IModelInfoExtensions
{
public static IEnumerable<T> GetCustomAttributes<T>(
this IModelInfo source) where T : Attribute
{
if (source != null)
{
return source.MemberInfo.GetCustomAttributes<T>();
}

return new List<T>();
}
}
}
20 changes: 20 additions & 0 deletions src/CloneExtensions/Extensions/MethodInfoExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System.Reflection;

namespace CloneExtensions.Extensions
{
public static class MethodInfoExtensions
{
public static bool HasAttribute(
this MethodInfo source,
MethodAttributes attr,
bool defaultValue = false)
{
if (source != null)
{
return (source.Attributes & attr) == attr;
}

return defaultValue;
}
}
}
Loading