diff --git a/src/Shared/PropertyActivator/PropertyActivator.cs b/src/Shared/PropertyActivator/PropertyActivator.cs deleted file mode 100644 index 925f6a76ae..0000000000 --- a/src/Shared/PropertyActivator/PropertyActivator.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Linq; -using System.Reflection; - -namespace Microsoft.Extensions.Internal -{ - internal class PropertyActivator - { - private readonly Func _valueAccessor; - private readonly Action _fastPropertySetter; - - public PropertyActivator( - PropertyInfo propertyInfo, - Func valueAccessor) - { - if (propertyInfo == null) - { - throw new ArgumentNullException(nameof(propertyInfo)); - } - - if (valueAccessor == null) - { - throw new ArgumentNullException(nameof(valueAccessor)); - } - - PropertyInfo = propertyInfo; - _valueAccessor = valueAccessor; - _fastPropertySetter = PropertyHelper.MakeFastPropertySetter(propertyInfo); - } - - public PropertyInfo PropertyInfo { get; private set; } - - public object Activate(object instance, TContext context) - { - if (instance == null) - { - throw new ArgumentNullException(nameof(instance)); - } - - var value = _valueAccessor(context); - _fastPropertySetter(instance, value); - return value; - } - - public static PropertyActivator[] GetPropertiesToActivate( - Type type, - Type activateAttributeType, - Func> createActivateInfo) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - if (activateAttributeType == null) - { - throw new ArgumentNullException(nameof(activateAttributeType)); - } - - if (createActivateInfo == null) - { - throw new ArgumentNullException(nameof(createActivateInfo)); - } - - return GetPropertiesToActivate(type, activateAttributeType, createActivateInfo, includeNonPublic: false); - } - - public static PropertyActivator[] GetPropertiesToActivate( - Type type, - Type activateAttributeType, - Func> createActivateInfo, - bool includeNonPublic) - { - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } - - if (activateAttributeType == null) - { - throw new ArgumentNullException(nameof(activateAttributeType)); - } - - if (createActivateInfo == null) - { - throw new ArgumentNullException(nameof(createActivateInfo)); - } - - var properties = type.GetRuntimeProperties() - .Where((property) => - { - return - property.IsDefined(activateAttributeType) && - property.GetIndexParameters().Length == 0 && - property.SetMethod != null && - !property.SetMethod.IsStatic; - }); - - if (!includeNonPublic) - { - properties = properties.Where(property => property.SetMethod.IsPublic); - } - - return properties.Select(createActivateInfo).ToArray(); - } - } -} \ No newline at end of file diff --git a/src/Shared/PropertyHelper/PropertyHelper.cs b/src/Shared/PropertyHelper/PropertyHelper.cs deleted file mode 100644 index 27ba5661a4..0000000000 --- a/src/Shared/PropertyHelper/PropertyHelper.cs +++ /dev/null @@ -1,526 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; - -namespace Microsoft.Extensions.Internal -{ - internal class PropertyHelper - { - // Delegate type for a by-ref property getter - private delegate TValue ByRefFunc(ref TDeclaringType arg); - - private static readonly MethodInfo CallPropertyGetterOpenGenericMethod = - typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetter)); - - private static readonly MethodInfo CallPropertyGetterByReferenceOpenGenericMethod = - typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertyGetterByReference)); - - private static readonly MethodInfo CallNullSafePropertyGetterOpenGenericMethod = - typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetter)); - - private static readonly MethodInfo CallNullSafePropertyGetterByReferenceOpenGenericMethod = - typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallNullSafePropertyGetterByReference)); - - private static readonly MethodInfo CallPropertySetterOpenGenericMethod = - typeof(PropertyHelper).GetTypeInfo().GetDeclaredMethod(nameof(CallPropertySetter)); - - // Using an array rather than IEnumerable, as target will be called on the hot path numerous times. - private static readonly ConcurrentDictionary PropertiesCache = - new ConcurrentDictionary(); - - private static readonly ConcurrentDictionary VisiblePropertiesCache = - new ConcurrentDictionary(); - - private Action _valueSetter; - private Func _valueGetter; - - /// - /// Initializes a fast . - /// This constructor does not cache the helper. For caching, use . - /// - public PropertyHelper(PropertyInfo property) - { - if (property == null) - { - throw new ArgumentNullException(nameof(property)); - } - - Property = property; - Name = property.Name; - } - - /// - /// Gets the backing . - /// - public PropertyInfo Property { get; } - - /// - /// Gets (or sets in derived types) the property name. - /// - public virtual string Name { get; protected set; } - - /// - /// Gets the property value getter. - /// - public Func ValueGetter - { - get - { - if (_valueGetter == null) - { - _valueGetter = MakeFastPropertyGetter(Property); - } - - return _valueGetter; - } - } - - /// - /// Gets the property value setter. - /// - public Action ValueSetter - { - get - { - if (_valueSetter == null) - { - _valueSetter = MakeFastPropertySetter(Property); - } - - return _valueSetter; - } - } - - /// - /// Returns the property value for the specified . - /// - /// The object whose property value will be returned. - /// The property value. - public object GetValue(object instance) - { - return ValueGetter(instance); - } - - /// - /// Sets the property value for the specified . - /// - /// The object whose property value will be set. - /// The property value. - public void SetValue(object instance, object value) - { - ValueSetter(instance, value); - } - - /// - /// Creates and caches fast property helpers that expose getters for every public get property on the - /// underlying type. - /// - /// The type info to extract property accessors for. - /// A cached array of all public properties of the specified type. - /// - public static PropertyHelper[] GetProperties(TypeInfo typeInfo) - { - return GetProperties(typeInfo.AsType()); - } - - /// - /// Creates and caches fast property helpers that expose getters for every public get property on the - /// specified type. - /// - /// The type to extract property accessors for. - /// A cached array of all public properties of the specified type. - /// - public static PropertyHelper[] GetProperties(Type type) - { - return GetProperties(type, CreateInstance, PropertiesCache); - } - - /// - /// - /// Creates and caches fast property helpers that expose getters for every non-hidden get property - /// on the specified type. - /// - /// - /// excludes properties defined on base types that have been - /// hidden by definitions using the new keyword. - /// - /// - /// The type info to extract property accessors for. - /// - /// A cached array of all public properties of the specified type. - /// - public static PropertyHelper[] GetVisibleProperties(TypeInfo typeInfo) - { - return GetVisibleProperties(typeInfo.AsType(), CreateInstance, PropertiesCache, VisiblePropertiesCache); - } - - /// - /// - /// Creates and caches fast property helpers that expose getters for every non-hidden get property - /// on the specified type. - /// - /// - /// excludes properties defined on base types that have been - /// hidden by definitions using the new keyword. - /// - /// - /// The type to extract property accessors for. - /// - /// A cached array of all public properties of the specified type. - /// - public static PropertyHelper[] GetVisibleProperties(Type type) - { - return GetVisibleProperties(type, CreateInstance, PropertiesCache, VisiblePropertiesCache); - } - - /// - /// Creates a single fast property getter. The result is not cached. - /// - /// propertyInfo to extract the getter for. - /// a fast getter. - /// - /// This method is more memory efficient than a dynamically compiled lambda, and about the - /// same speed. - /// - public static Func MakeFastPropertyGetter(PropertyInfo propertyInfo) - { - Debug.Assert(propertyInfo != null); - - return MakeFastPropertyGetter( - propertyInfo, - CallPropertyGetterOpenGenericMethod, - CallPropertyGetterByReferenceOpenGenericMethod); - } - - /// - /// Creates a single fast property getter which is safe for a null input object. The result is not cached. - /// - /// propertyInfo to extract the getter for. - /// a fast getter. - /// - /// This method is more memory efficient than a dynamically compiled lambda, and about the - /// same speed. - /// - public static Func MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo) - { - Debug.Assert(propertyInfo != null); - - return MakeFastPropertyGetter( - propertyInfo, - CallNullSafePropertyGetterOpenGenericMethod, - CallNullSafePropertyGetterByReferenceOpenGenericMethod); - } - - private static Func MakeFastPropertyGetter( - PropertyInfo propertyInfo, - MethodInfo propertyGetterWrapperMethod, - MethodInfo propertyGetterByRefWrapperMethod) - { - Debug.Assert(propertyInfo != null); - - // Must be a generic method with a Func<,> parameter - Debug.Assert(propertyGetterWrapperMethod != null); - Debug.Assert(propertyGetterWrapperMethod.IsGenericMethodDefinition); - Debug.Assert(propertyGetterWrapperMethod.GetParameters().Length == 2); - - // Must be a generic method with a ByRefFunc<,> parameter - Debug.Assert(propertyGetterByRefWrapperMethod != null); - Debug.Assert(propertyGetterByRefWrapperMethod.IsGenericMethodDefinition); - Debug.Assert(propertyGetterByRefWrapperMethod.GetParameters().Length == 2); - - var getMethod = propertyInfo.GetMethod; - Debug.Assert(getMethod != null); - Debug.Assert(!getMethod.IsStatic); - Debug.Assert(getMethod.GetParameters().Length == 0); - - // Instance methods in the CLR can be turned into static methods where the first parameter - // is open over "target". This parameter is always passed by reference, so we have a code - // path for value types and a code path for reference types. - if (getMethod.DeclaringType.GetTypeInfo().IsValueType) - { - // Create a delegate (ref TDeclaringType) -> TValue - return MakeFastPropertyGetter( - typeof(ByRefFunc<,>), - getMethod, - propertyGetterByRefWrapperMethod); - } - else - { - // Create a delegate TDeclaringType -> TValue - return MakeFastPropertyGetter( - typeof(Func<,>), - getMethod, - propertyGetterWrapperMethod); - } - } - - private static Func MakeFastPropertyGetter( - Type openGenericDelegateType, - MethodInfo propertyGetMethod, - MethodInfo openGenericWrapperMethod) - { - var typeInput = propertyGetMethod.DeclaringType; - var typeOutput = propertyGetMethod.ReturnType; - - var delegateType = openGenericDelegateType.MakeGenericType(typeInput, typeOutput); - var propertyGetterDelegate = propertyGetMethod.CreateDelegate(delegateType); - - var wrapperDelegateMethod = openGenericWrapperMethod.MakeGenericMethod(typeInput, typeOutput); - var accessorDelegate = wrapperDelegateMethod.CreateDelegate( - typeof(Func), - propertyGetterDelegate); - - return (Func)accessorDelegate; - } - - /// - /// Creates a single fast property setter for reference types. The result is not cached. - /// - /// propertyInfo to extract the setter for. - /// a fast getter. - /// - /// This method is more memory efficient than a dynamically compiled lambda, and about the - /// same speed. This only works for reference types. - /// - public static Action MakeFastPropertySetter(PropertyInfo propertyInfo) - { - Debug.Assert(propertyInfo != null); - Debug.Assert(!propertyInfo.DeclaringType.GetTypeInfo().IsValueType); - - var setMethod = propertyInfo.SetMethod; - Debug.Assert(setMethod != null); - Debug.Assert(!setMethod.IsStatic); - Debug.Assert(setMethod.ReturnType == typeof(void)); - var parameters = setMethod.GetParameters(); - Debug.Assert(parameters.Length == 1); - - // Instance methods in the CLR can be turned into static methods where the first parameter - // is open over "target". This parameter is always passed by reference, so we have a code - // path for value types and a code path for reference types. - var typeInput = setMethod.DeclaringType; - var parameterType = parameters[0].ParameterType; - - // Create a delegate TDeclaringType -> { TDeclaringType.Property = TValue; } - var propertySetterAsAction = - setMethod.CreateDelegate(typeof(Action<,>).MakeGenericType(typeInput, parameterType)); - var callPropertySetterClosedGenericMethod = - CallPropertySetterOpenGenericMethod.MakeGenericMethod(typeInput, parameterType); - var callPropertySetterDelegate = - callPropertySetterClosedGenericMethod.CreateDelegate( - typeof(Action), propertySetterAsAction); - - return (Action)callPropertySetterDelegate; - } - - /// - /// Given an object, adds each instance property with a public get method as a key and its - /// associated value to a dictionary. - /// - /// If the object is already an instance, then a copy - /// is returned. - /// - /// - /// The implementation of PropertyHelper will cache the property accessors per-type. This is - /// faster when the same type is used multiple times with ObjectToDictionary. - /// - public static IDictionary ObjectToDictionary(object value) - { - var dictionary = value as IDictionary; - if (dictionary != null) - { - return new Dictionary(dictionary, StringComparer.OrdinalIgnoreCase); - } - - dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); - - if (value != null) - { - foreach (var helper in GetProperties(value.GetType())) - { - dictionary[helper.Name] = helper.GetValue(value); - } - } - - return dictionary; - } - - private static PropertyHelper CreateInstance(PropertyInfo property) - { - return new PropertyHelper(property); - } - - // Called via reflection - private static object CallPropertyGetter( - Func getter, - object target) - { - return getter((TDeclaringType)target); - } - - // Called via reflection - private static object CallPropertyGetterByReference( - ByRefFunc getter, - object target) - { - var unboxed = (TDeclaringType)target; - return getter(ref unboxed); - } - - // Called via reflection - private static object CallNullSafePropertyGetter( - Func getter, - object target) - { - if (target == null) - { - return null; - } - - return getter((TDeclaringType)target); - } - - // Called via reflection - private static object CallNullSafePropertyGetterByReference( - ByRefFunc getter, - object target) - { - if (target == null) - { - return null; - } - - var unboxed = (TDeclaringType)target; - return getter(ref unboxed); - } - - private static void CallPropertySetter( - Action setter, - object target, - object value) - { - setter((TDeclaringType)target, (TValue)value); - } - - protected static PropertyHelper[] GetVisibleProperties( - Type type, - Func createPropertyHelper, - ConcurrentDictionary allPropertiesCache, - ConcurrentDictionary visiblePropertiesCache) - { - PropertyHelper[] result; - if (visiblePropertiesCache.TryGetValue(type, out result)) - { - return result; - } - - // The simple and common case, this is normal POCO object - no need to allocate. - var allPropertiesDefinedOnType = true; - var allProperties = GetProperties(type, createPropertyHelper, allPropertiesCache); - foreach (var propertyHelper in allProperties) - { - if (propertyHelper.Property.DeclaringType != type) - { - allPropertiesDefinedOnType = false; - break; - } - } - - if (allPropertiesDefinedOnType) - { - result = allProperties; - visiblePropertiesCache.TryAdd(type, result); - return result; - } - - // There's some inherited properties here, so we need to check for hiding via 'new'. - var filteredProperties = new List(allProperties.Length); - foreach (var propertyHelper in allProperties) - { - var declaringType = propertyHelper.Property.DeclaringType; - if (declaringType == type) - { - filteredProperties.Add(propertyHelper); - continue; - } - - // If this property was declared on a base type then look for the definition closest to the - // the type to see if we should include it. - var ignoreProperty = false; - - // Walk up the hierarchy until we find the type that actually declares this - // PropertyInfo. - var currentTypeInfo = type.GetTypeInfo(); - var declaringTypeInfo = declaringType.GetTypeInfo(); - while (currentTypeInfo != null && currentTypeInfo != declaringTypeInfo) - { - // We've found a 'more proximal' public definition - var declaredProperty = currentTypeInfo.GetDeclaredProperty(propertyHelper.Name); - if (declaredProperty != null) - { - ignoreProperty = true; - break; - } - - currentTypeInfo = currentTypeInfo.BaseType?.GetTypeInfo(); - } - - if (!ignoreProperty) - { - filteredProperties.Add(propertyHelper); - } - } - - result = filteredProperties.ToArray(); - visiblePropertiesCache.TryAdd(type, result); - return result; - } - - protected static PropertyHelper[] GetProperties( - Type type, - Func createPropertyHelper, - ConcurrentDictionary cache) - { - // Unwrap nullable types. This means Nullable.Value and Nullable.HasValue will not be - // part of the sequence of properties returned by this method. - type = Nullable.GetUnderlyingType(type) ?? type; - - PropertyHelper[] helpers; - if (!cache.TryGetValue(type, out helpers)) - { - // We avoid loading indexed properties using the Where statement. - var properties = type.GetRuntimeProperties().Where(IsInterestingProperty); - - var typeInfo = type.GetTypeInfo(); - if (typeInfo.IsInterface) - { - // Reflection does not return information about inherited properties on the interface itself. - properties = properties.Concat(typeInfo.ImplementedInterfaces.SelectMany( - interfaceType => interfaceType.GetRuntimeProperties().Where(IsInterestingProperty))); - } - - helpers = properties.Select(p => createPropertyHelper(p)).ToArray(); - cache.TryAdd(type, helpers); - } - - return helpers; - } - - // Indexed properties are not useful (or valid) for grabbing properties off an object. - private static bool IsInterestingProperty(PropertyInfo property) - { - // For improving application startup time, do not use GetIndexParameters() api early in this check as it - // creates a copy of parameter array and also we would like to check for the presence of a get method - // and short circuit asap. - return property.GetMethod != null && - property.GetMethod.IsPublic && - !property.GetMethod.IsStatic && - property.GetMethod.GetParameters().Length == 0; - } - } -} diff --git a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj index 05b321a6d6..67ff52821b 100644 --- a/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj +++ b/src/Shared/test/Shared.Tests/Microsoft.AspNetCore.Shared.Tests.csproj @@ -21,7 +21,6 @@ - diff --git a/src/Shared/test/Shared.Tests/PropertyActivatorTest.cs b/src/Shared/test/Shared.Tests/PropertyActivatorTest.cs deleted file mode 100644 index a5cb1605b3..0000000000 --- a/src/Shared/test/Shared.Tests/PropertyActivatorTest.cs +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Reflection; -using Xunit; - -namespace Microsoft.Extensions.Internal -{ - public class PropertyActivatorTest - { - [Fact] - public void Activate_InvokesValueAccessorWithExpectedValue() - { - // Arrange - var instance = new TestClass(); - var typeInfo = instance.GetType().GetTypeInfo(); - var property = typeInfo.GetDeclaredProperty("IntProperty"); - var invokedWith = -1; - var activator = new PropertyActivator( - property, - valueAccessor: (val) => - { - invokedWith = val; - return val; - }); - - // Act - activator.Activate(instance, 123); - - // Assert - Assert.Equal(123, invokedWith); - } - - [Fact] - public void Activate_SetsPropertyValue() - { - // Arrange - var instance = new TestClass(); - var typeInfo = instance.GetType().GetTypeInfo(); - var property = typeInfo.GetDeclaredProperty("IntProperty"); - var activator = new PropertyActivator(property, valueAccessor: (val) => val + 1); - - // Act - activator.Activate(instance, 123); - - // Assert - Assert.Equal(124, instance.IntProperty); - } - - [Fact] - public void GetPropertiesToActivate_RestrictsActivatableProperties() - { - // Arrange - var instance = new TestClass(); - var typeInfo = instance.GetType().GetTypeInfo(); - var expectedPropertyInfo = typeInfo.GetDeclaredProperty("ActivatableProperty"); - - // Act - var propertiesToActivate = PropertyActivator.GetPropertiesToActivate( - type: typeof(TestClass), - activateAttributeType: typeof(TestActivateAttribute), - createActivateInfo: - (propertyInfo) => new PropertyActivator(propertyInfo, valueAccessor: (val) => val + 1)); - - // Assert - Assert.Collection( - propertiesToActivate, - (activator) => - { - Assert.Equal(expectedPropertyInfo, activator.PropertyInfo); - }); - } - - [Fact] - public void GetPropertiesToActivate_CanCreateCustomPropertyActivators() - { - // Arrange - var instance = new TestClass(); - var typeInfo = instance.GetType().GetTypeInfo(); - var expectedPropertyInfo = typeInfo.GetDeclaredProperty("IntProperty"); - - // Act - var propertiesToActivate = PropertyActivator.GetPropertiesToActivate( - type: typeof(TestClass), - activateAttributeType: typeof(TestActivateAttribute), - createActivateInfo: - (propertyInfo) => new PropertyActivator(expectedPropertyInfo, valueAccessor: (val) => val + 1)); - - // Assert - Assert.Collection( - propertiesToActivate, - (activator) => - { - Assert.Equal(expectedPropertyInfo, activator.PropertyInfo); - }); - } - - [Fact] - public void GetPropertiesToActivate_ExcludesNonPublic() - { - // Arrange - var instance = new TestClassWithPropertyVisiblity(); - var typeInfo = instance.GetType().GetTypeInfo(); - var expectedPropertyInfo = typeInfo.GetDeclaredProperty("Public"); - - // Act - var propertiesToActivate = PropertyActivator.GetPropertiesToActivate( - typeof(TestClassWithPropertyVisiblity), - typeof(TestActivateAttribute), - (propertyInfo) => new PropertyActivator(propertyInfo, valueAccessor: (val) => val)); - - // Assert - Assert.Single(propertiesToActivate); - Assert.Single(propertiesToActivate, p => p.PropertyInfo == expectedPropertyInfo); - } - - [Fact] - public void GetPropertiesToActivate_IncludesNonPublic() - { - // Arrange - var instance = new TestClassWithPropertyVisiblity(); - var typeInfo = instance.GetType().GetTypeInfo(); - - // Act - var propertiesToActivate = PropertyActivator.GetPropertiesToActivate( - typeof(TestClassWithPropertyVisiblity), - typeof(TestActivateAttribute), - (propertyInfo) => new PropertyActivator(propertyInfo, valueAccessor: (val) => val), - includeNonPublic: true); - - // Assert - Assert.Equal(5, propertiesToActivate.Length); - } - - private class TestClass - { - public int IntProperty { get; set; } - - [TestActivate] - public int ActivatableProperty { get; set; } - - [TestActivate] - public int NoSetterActivatableProperty { get; } - - [TestActivate] - public int this[int something] // Not activatable - { - get - { - return 0; - } - } - - [TestActivate] - public static int StaticActivatablProperty { get; set; } - } - - private class TestClassWithPropertyVisiblity - { - [TestActivate] - public int Public { get; set; } - - [TestActivate] - protected int Protected { get; set; } - - [TestActivate] - internal int Internal { get; set; } - - [TestActivate] - protected internal int ProtectedInternal {get; set; } - - [TestActivate] - private int Private { get; set; } - } - - [AttributeUsage(AttributeTargets.Property)] - private class TestActivateAttribute : Attribute - { - } - - private class ActivationInfo - { - public string Name { get; set; } - } - } -} diff --git a/src/Shared/test/Shared.Tests/PropertyHelperTest.cs b/src/Shared/test/Shared.Tests/PropertyHelperTest.cs deleted file mode 100644 index 19cf08b370..0000000000 --- a/src/Shared/test/Shared.Tests/PropertyHelperTest.cs +++ /dev/null @@ -1,831 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using Xunit; - -namespace Microsoft.Extensions.Internal -{ - public class PropertyHelperTest - { - [Fact] - public void PropertyHelper_ReturnsNameCorrectly() - { - // Arrange - var anonymous = new { foo = "bar" }; - var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property; - - // Act - var helper = new PropertyHelper(property); - - // Assert - Assert.Equal("foo", property.Name); - Assert.Equal("foo", helper.Name); - } - - [Fact] - public void PropertyHelper_ReturnsValueCorrectly() - { - // Arrange - var anonymous = new { bar = "baz" }; - var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property; - - // Act - var helper = new PropertyHelper(property); - - // Assert - Assert.Equal("bar", helper.Name); - Assert.Equal("baz", helper.GetValue(anonymous)); - } - - [Fact] - public void PropertyHelper_ReturnsGetterDelegate() - { - // Arrange - var anonymous = new { bar = "baz" }; - var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property; - - // Act - var helper = new PropertyHelper(property); - - // Assert - Assert.NotNull(helper.ValueGetter); - Assert.Equal("baz", helper.ValueGetter(anonymous)); - } - - [Fact] - public void SetValue_SetsPropertyValue() - { - // Arrange - var expected = "new value"; - var instance = new BaseClass { PropA = "old value" }; - var helper = PropertyHelper.GetProperties( - instance.GetType()).First(prop => prop.Name == "PropA"); - - // Act - helper.SetValue(instance, expected); - - // Assert - Assert.Equal(expected, instance.PropA); - } - - [Fact] - public void PropertyHelper_ReturnsSetterDelegate() - { - // Arrange - var expected = "new value"; - var instance = new BaseClass { PropA = "old value" }; - var helper = PropertyHelper.GetProperties( - instance.GetType()).First(prop => prop.Name == "PropA"); - - // Act and Assert - Assert.NotNull(helper.ValueSetter); - helper.ValueSetter(instance, expected); - - // Assert - Assert.Equal(expected, instance.PropA); - } - - [Fact] - public void PropertyHelper_ReturnsValueCorrectly_ForValueTypes() - { - // Arrange - var anonymous = new { foo = 32 }; - var property = PropertyHelper.GetProperties(anonymous.GetType()).First().Property; - - // Act - var helper = new PropertyHelper(property); - - // Assert - Assert.Equal("foo", helper.Name); - Assert.Equal(32, helper.GetValue(anonymous)); - } - - [Fact] - public void PropertyHelper_ReturnsCachedPropertyHelper() - { - // Arrange - var anonymous = new { foo = "bar" }; - - // Act - var helpers1 = PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()); - var helpers2 = PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()); - - // Assert - Assert.Single(helpers1); - Assert.Same(helpers1, helpers2); - Assert.Same(helpers1[0], helpers2[0]); - } - - [Fact] - public void PropertyHelper_DoesNotChangeUnderscores() - { - // Arrange - var anonymous = new { bar_baz2 = "foo" }; - - // Act + Assert - var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo())); - Assert.Equal("bar_baz2", helper.Name); - } - - [Fact] - public void PropertyHelper_DoesNotFindPrivateProperties() - { - // Arrange - var anonymous = new PrivateProperties(); - - // Act + Assert - var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo())); - Assert.Equal("Prop1", helper.Name); - } - - [Fact] - public void PropertyHelper_DoesNotFindStaticProperties() - { - // Arrange - var anonymous = new Static(); - - // Act + Assert - var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo())); - Assert.Equal("Prop5", helper.Name); - } - - [Fact] - public void PropertyHelper_DoesNotFindSetOnlyProperties() - { - // Arrange - var anonymous = new SetOnly(); - - // Act + Assert - var helper = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo())); - Assert.Equal("Prop6", helper.Name); - } - - [Theory] - [InlineData(typeof(int?))] - [InlineData(typeof(DayOfWeek?))] - public void PropertyHelper_WorksForNullablePrimitiveAndEnumTypes(Type nullableType) - { - // Act - var properties = PropertyHelper.GetProperties(nullableType); - - // Assert - Assert.Empty(properties); - } - - [Fact] - public void PropertyHelper_UnwrapsNullableTypes() - { - // Arrange - var myType = typeof(MyStruct?); - - // Act - var properties = PropertyHelper.GetProperties(myType); - - // Assert - var property = Assert.Single(properties); - Assert.Equal("Foo", property.Name); - } - - [Fact] - public void PropertyHelper_WorksForStruct() - { - // Arrange - var anonymous = new MyProperties(); - - anonymous.IntProp = 3; - anonymous.StringProp = "Five"; - - // Act + Assert - var helper1 = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()).Where(prop => prop.Name == "IntProp")); - var helper2 = Assert.Single(PropertyHelper.GetProperties(anonymous.GetType().GetTypeInfo()).Where(prop => prop.Name == "StringProp")); - Assert.Equal(3, helper1.GetValue(anonymous)); - Assert.Equal("Five", helper2.GetValue(anonymous)); - } - - [Fact] - public void PropertyHelper_ForDerivedClass() - { - // Arrange - var derived = new DerivedClass { PropA = "propAValue", PropB = "propBValue" }; - - // Act - var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray(); - - // Assert - Assert.NotNull(helpers); - Assert.Equal(2, helpers.Length); - - var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA")); - var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB")); - - Assert.Equal("propAValue", propAHelper.GetValue(derived)); - Assert.Equal("propBValue", propBHelper.GetValue(derived)); - } - - [Fact] - public void PropertyHelper_ForDerivedClass_WithNew() - { - // Arrange - var derived = new DerivedClassWithNew { PropA = "propAValue" }; - - // Act - var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray(); - - // Assert - Assert.NotNull(helpers); - Assert.Equal(2, helpers.Length); - - var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA")); - var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB")); - - Assert.Equal("propAValue", propAHelper.GetValue(derived)); - Assert.Equal("Newed", propBHelper.GetValue(derived)); - } - - [Fact] - public void PropertyHelper_ForDerived_WithVirtual() - { - // Arrange - var derived = new DerivedClassWithOverride { PropA = "propAValue", PropB = "propBValue" }; - - // Act - var helpers = PropertyHelper.GetProperties(derived.GetType().GetTypeInfo()).ToArray(); - - // Assert - Assert.NotNull(helpers); - Assert.Equal(2, helpers.Length); - - var propAHelper = Assert.Single(helpers.Where(h => h.Name == "PropA")); - var propBHelper = Assert.Single(helpers.Where(h => h.Name == "PropB")); - - Assert.Equal("OverridenpropAValue", propAHelper.GetValue(derived)); - Assert.Equal("propBValue", propBHelper.GetValue(derived)); - } - - [Fact] - public void PropertyHelper_ForInterface_ReturnsExpectedProperties() - { - // Arrange - var expectedNames = new[] { "Count", "IsReadOnly" }; - - // Act - var helpers = PropertyHelper.GetProperties(typeof(ICollection)); - - // Assert - Assert.Collection( - helpers.OrderBy(helper => helper.Name, StringComparer.Ordinal), - helper => { Assert.Equal(expectedNames[0], helper.Name, StringComparer.Ordinal); }, - helper => { Assert.Equal(expectedNames[1], helper.Name, StringComparer.Ordinal); }); - } - - [Fact] - public void PropertyHelper_ForDerivedInterface_ReturnsAllProperties() - { - // Arrange - var expectedNames = new[] { "Count", "IsReadOnly", "Keys", "Values" }; - - // Act - var helpers = PropertyHelper.GetProperties(typeof(IDictionary)); - - // Assert - Assert.Collection( - helpers.OrderBy(helper => helper.Name, StringComparer.Ordinal), - helper => { Assert.Equal(expectedNames[0], helper.Name, StringComparer.Ordinal); }, - helper => { Assert.Equal(expectedNames[1], helper.Name, StringComparer.Ordinal); }, - helper => { Assert.Equal(expectedNames[2], helper.Name, StringComparer.Ordinal); }, - helper => { Assert.Equal(expectedNames[3], helper.Name, StringComparer.Ordinal); }); - } - - [Fact] - public void GetProperties_ExcludesIndexersAndPropertiesWithoutPublicGetters() - { - // Arrange - var type = typeof(DerivedClassWithNonReadableProperties); - - // Act - var result = PropertyHelper.GetProperties(type).ToArray(); - - // Assert - Assert.Equal(3, result.Length); - Assert.Equal("Visible", result[0].Name); - Assert.Equal("PropA", result[1].Name); - Assert.Equal("PropB", result[2].Name); - } - - [Fact] - public void GetVisibleProperties_NoHiddenProperty() - { - // Arrange - var type = typeof(string); - - // Act - var result = PropertyHelper.GetVisibleProperties(type).ToArray(); - - // Assert - var property = Assert.Single(result); - Assert.Equal("Length", property.Name); - Assert.Equal(typeof(int), property.Property.PropertyType); - } - - [Fact] - public void GetVisibleProperties_HiddenProperty() - { - // Arrange - var type = typeof(DerivedHiddenProperty); - - // Act - var result = PropertyHelper.GetVisibleProperties(type).ToArray(); - - // Assert - Assert.Equal(2, result.Length); - Assert.Equal("Id", result[0].Name); - Assert.Equal(typeof(string), result[0].Property.PropertyType); - Assert.Equal("Name", result[1].Name); - Assert.Equal(typeof(string), result[1].Property.PropertyType); - } - - [Fact] - public void GetVisibleProperties_HiddenProperty_TwoLevels() - { - // Arrange - var type = typeof(DerivedHiddenProperty2); - - // Act - var result = PropertyHelper.GetVisibleProperties(type).ToArray(); - - // Assert - Assert.Equal(2, result.Length); - Assert.Equal("Id", result[0].Name); - Assert.Equal(typeof(Guid), result[0].Property.PropertyType); - Assert.Equal("Name", result[1].Name); - Assert.Equal(typeof(string), result[1].Property.PropertyType); - } - - [Fact] - public void GetVisibleProperties_NoHiddenPropertyWithTypeInfoInput() - { - // Arrange - var type = typeof(string); - - // Act - var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray(); - - // Assert - var property = Assert.Single(result); - Assert.Equal("Length", property.Name); - Assert.Equal(typeof(int), property.Property.PropertyType); - } - - [Fact] - public void GetVisibleProperties_HiddenPropertyWithTypeInfoInput() - { - // Arrange - var type = typeof(DerivedHiddenProperty); - - // Act - var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray(); - - // Assert - Assert.Equal(2, result.Length); - Assert.Equal("Id", result[0].Name); - Assert.Equal(typeof(string), result[0].Property.PropertyType); - Assert.Equal("Name", result[1].Name); - Assert.Equal(typeof(string), result[1].Property.PropertyType); - } - - [Fact] - public void GetVisibleProperties_HiddenProperty_TwoLevelsWithTypeInfoInput() - { - // Arrange - var type = typeof(DerivedHiddenProperty2); - - // Act - var result = PropertyHelper.GetVisibleProperties(type.GetTypeInfo()).ToArray(); - - // Assert - Assert.Equal(2, result.Length); - Assert.Equal("Id", result[0].Name); - Assert.Equal(typeof(Guid), result[0].Property.PropertyType); - Assert.Equal("Name", result[1].Name); - Assert.Equal(typeof(string), result[1].Property.PropertyType); - } - - [Fact] - public void MakeFastPropertySetter_SetsPropertyValues_ForPublicAndNobPublicProperties() - { - // Arrange - var instance = new BaseClass(); - var typeInfo = instance.GetType().GetTypeInfo(); - var publicProperty = typeInfo.GetDeclaredProperty("PropA"); - var protectedProperty = typeInfo.GetDeclaredProperty("PropProtected"); - var publicPropertySetter = PropertyHelper.MakeFastPropertySetter(publicProperty); - var protectedPropertySetter = PropertyHelper.MakeFastPropertySetter(protectedProperty); - - // Act - publicPropertySetter(instance, "TestPublic"); - protectedPropertySetter(instance, "TestProtected"); - - // Assert - Assert.Equal("TestPublic", instance.PropA); - Assert.Equal("TestProtected", instance.GetPropProtected()); - } - - [Fact] - public void MakeFastPropertySetter_SetsPropertyValues_ForOverridenProperties() - { - // Arrange - var instance = new DerivedClassWithOverride(); - var typeInfo = instance.GetType().GetTypeInfo(); - var property = typeInfo.GetDeclaredProperty("PropA"); - var propertySetter = PropertyHelper.MakeFastPropertySetter(property); - - // Act - propertySetter(instance, "Test value"); - - // Assert - Assert.Equal("OverridenTest value", instance.PropA); - } - - [Fact] - public void MakeFastPropertySetter_SetsPropertyValues_ForNewedProperties() - { - // Arrange - var instance = new DerivedClassWithNew(); - var typeInfo = instance.GetType().GetTypeInfo(); - var property = typeInfo.GetDeclaredProperty("PropB"); - var propertySetter = PropertyHelper.MakeFastPropertySetter(property); - - // Act - propertySetter(instance, "Test value"); - - // Assert - Assert.Equal("NewedTest value", instance.PropB); - } - - [Fact] - public void MakeFastPropertyGetter_ReferenceType_ForNullObject_Throws() - { - // Arrange - var property = PropertyHelper - .GetProperties(typeof(BaseClass)) - .Single(p => p.Name == nameof(BaseClass.PropA)); - - var accessor = PropertyHelper.MakeFastPropertyGetter(property.Property); - - // Act & Assert - Assert.Throws(() => accessor(null)); - } - - [Fact] - public void MakeFastPropertyGetter_ValueType_ForNullObject_Throws() - { - // Arrange - var property = PropertyHelper - .GetProperties(typeof(MyProperties)) - .Single(p => p.Name == nameof(MyProperties.StringProp)); - - var accessor = PropertyHelper.MakeFastPropertyGetter(property.Property); - - // Act & Assert - Assert.Throws(() => accessor(null)); - } - - [Fact] - public void MakeNullSafeFastPropertyGetter_ReferenceType_Success() - { - // Arrange - var property = PropertyHelper - .GetProperties(typeof(BaseClass)) - .Single(p => p.Name == nameof(BaseClass.PropA)); - - var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property); - - // Act - var value = accessor(new BaseClass() { PropA = "Hi" }); - - // Assert - Assert.Equal("Hi", value); - } - - [Fact] - public void MakeNullSafeFastPropertyGetter_ValueType_Success() - { - // Arrange - var property = PropertyHelper - .GetProperties(typeof(MyProperties)) - .Single(p => p.Name == nameof(MyProperties.StringProp)); - - var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property); - - // Act - var value = accessor(new MyProperties() { StringProp = "Hi" }); - - // Assert - Assert.Equal("Hi", value); - } - - [Fact] - public void MakeNullSafeFastPropertyGetter_ReferenceType_ForNullObject_ReturnsNull() - { - // Arrange - var property = PropertyHelper - .GetProperties(typeof(BaseClass)) - .Single(p => p.Name == nameof(BaseClass.PropA)); - - var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property); - - // Act - var value = accessor(null); - - // Assert - Assert.Null(value); - } - - [Fact] - public void MakeNullSafeFastPropertyGetter_ValueType_ForNullObject_ReturnsNull() - { - // Arrange - var property = PropertyHelper - .GetProperties(typeof(MyProperties)) - .Single(p => p.Name == nameof(MyProperties.StringProp)); - - var accessor = PropertyHelper.MakeNullSafeFastPropertyGetter(property.Property); - - // Act - var value = accessor(null); - - // Assert - Assert.Null(value); - } - - public static TheoryData> IgnoreCaseTestData - { - get - { - return new TheoryData> - { - { - new - { - selected = true, - SeLeCtEd = false - }, - new KeyValuePair("selected", false) - }, - { - new - { - SeLeCtEd = false, - selected = true - }, - new KeyValuePair("SeLeCtEd", true) - }, - { - new - { - SelECTeD = false, - SeLECTED = true - }, - new KeyValuePair("SelECTeD", true) - } - }; - } - } - - [Theory] - [MemberData(nameof(IgnoreCaseTestData))] - public void ObjectToDictionary_IgnoresPropertyCase(object testObject, - KeyValuePair expectedEntry) - { - // Act - var result = PropertyHelper.ObjectToDictionary(testObject); - - // Assert - var entry = Assert.Single(result); - Assert.Equal(expectedEntry, entry); - } - - [Fact] - public void ObjectToDictionary_WithNullObject_ReturnsEmptyDictionary() - { - // Arrange - object value = null; - - // Act - var dictValues = PropertyHelper.ObjectToDictionary(value); - - // Assert - Assert.NotNull(dictValues); - Assert.Equal(0, dictValues.Count); - } - - [Fact] - public void ObjectToDictionary_WithPlainObjectType_ReturnsEmptyDictionary() - { - // Arrange - var value = new object(); - - // Act - var dictValues = PropertyHelper.ObjectToDictionary(value); - - // Assert - Assert.NotNull(dictValues); - Assert.Equal(0, dictValues.Count); - } - - [Fact] - public void ObjectToDictionary_WithPrimitiveType_LooksUpPublicProperties() - { - // Arrange - var value = "test"; - - // Act - var dictValues = PropertyHelper.ObjectToDictionary(value); - - // Assert - Assert.NotNull(dictValues); - Assert.Equal(1, dictValues.Count); - Assert.Equal(4, dictValues["Length"]); - } - - [Fact] - public void ObjectToDictionary_WithAnonymousType_LooksUpProperties() - { - // Arrange - var value = new { test = "value", other = 1 }; - - // Act - var dictValues = PropertyHelper.ObjectToDictionary(value); - - // Assert - Assert.NotNull(dictValues); - Assert.Equal(2, dictValues.Count); - Assert.Equal("value", dictValues["test"]); - Assert.Equal(1, dictValues["other"]); - } - - [Fact] - public void ObjectToDictionary_ReturnsCaseInsensitiveDictionary() - { - // Arrange - var value = new { TEST = "value", oThEr = 1 }; - - // Act - var dictValues = PropertyHelper.ObjectToDictionary(value); - - // Assert - Assert.NotNull(dictValues); - Assert.Equal(2, dictValues.Count); - Assert.Equal("value", dictValues["test"]); - Assert.Equal(1, dictValues["other"]); - } - - [Fact] - public void ObjectToDictionary_ReturnsInheritedProperties() - { - // Arrange - var value = new ThreeDPoint() { X = 5, Y = 10, Z = 17 }; - - // Act - var dictValues = PropertyHelper.ObjectToDictionary(value); - - // Assert - Assert.NotNull(dictValues); - Assert.Equal(3, dictValues.Count); - Assert.Equal(5, dictValues["X"]); - Assert.Equal(10, dictValues["Y"]); - Assert.Equal(17, dictValues["Z"]); - } - - private class Point - { - public int X { get; set; } - public int Y { get; set; } - } - - private class ThreeDPoint : Point - { - public int Z { get; set; } - } - - private class Static - { - public static int Prop2 { get; set; } - public int Prop5 { get; set; } - } - - private struct MyProperties - { - public int IntProp { get; set; } - public string StringProp { get; set; } - } - - private class SetOnly - { - public int Prop2 { set { } } - public int Prop6 { get; set; } - } - - private class PrivateProperties - { - public int Prop1 { get; set; } - protected int Prop2 { get; set; } - private int Prop3 { get; set; } - } - - public class BaseClass - { - public string PropA { get; set; } - - protected string PropProtected { get; set; } - - public string GetPropProtected() - { - return PropProtected; - } - } - - public class DerivedClass : BaseClass - { - public string PropB { get; set; } - } - - public class BaseClassWithVirtual - { - public virtual string PropA { get; set; } - public string PropB { get; set; } - } - - public class DerivedClassWithNew : BaseClassWithVirtual - { - private string _value = "Newed"; - - public new string PropB - { - get { return _value; } - set { _value = "Newed" + value; } - } - } - - public class DerivedClassWithOverride : BaseClassWithVirtual - { - private string _value = "Overriden"; - - public override string PropA - { - get { return _value; } - set { _value = "Overriden" + value; } - } - } - - private class DerivedClassWithNonReadableProperties : BaseClassWithVirtual - { - public string this[int index] - { - get { return string.Empty; } - set { } - } - - public int Visible { get; set; } - - private string NotVisible { get; set; } - - public string NotVisible2 { private get; set; } - - public string NotVisible3 - { - set { } - } - - public static string NotVisible4 { get; set; } - } - - private struct MyStruct - { - public string Foo { get; set; } - } - - private class BaseHiddenProperty - { - public int Id { get; set; } - } - - private class DerivedHiddenProperty : BaseHiddenProperty - { - public new string Id { get; set; } - - public string Name { get; set; } - } - - private class DerivedHiddenProperty2 : DerivedHiddenProperty - { - public new Guid Id { get; set; } - - public new string Name { get; private set; } - } - } -}