Remove Microsoft.Extensions.PropertyActivator.Sources and Microsoft.Extensions.PropertyHelper.Sources
\n\nCommit migrated from 6c11818fe9
This commit is contained in:
parent
bbf0ebdd91
commit
921060b2ca
|
|
@ -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<TContext>
|
||||
{
|
||||
private readonly Func<TContext, object> _valueAccessor;
|
||||
private readonly Action<object, object> _fastPropertySetter;
|
||||
|
||||
public PropertyActivator(
|
||||
PropertyInfo propertyInfo,
|
||||
Func<TContext, object> 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<TContext>[] GetPropertiesToActivate(
|
||||
Type type,
|
||||
Type activateAttributeType,
|
||||
Func<PropertyInfo, PropertyActivator<TContext>> 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<TContext>[] GetPropertiesToActivate(
|
||||
Type type,
|
||||
Type activateAttributeType,
|
||||
Func<PropertyInfo, PropertyActivator<TContext>> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TDeclaringType, TValue>(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<Type, PropertyHelper[]> PropertiesCache =
|
||||
new ConcurrentDictionary<Type, PropertyHelper[]>();
|
||||
|
||||
private static readonly ConcurrentDictionary<Type, PropertyHelper[]> VisiblePropertiesCache =
|
||||
new ConcurrentDictionary<Type, PropertyHelper[]>();
|
||||
|
||||
private Action<object, object> _valueSetter;
|
||||
private Func<object, object> _valueGetter;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a fast <see cref="PropertyHelper"/>.
|
||||
/// This constructor does not cache the helper. For caching, use <see cref="GetProperties(Type)"/>.
|
||||
/// </summary>
|
||||
public PropertyHelper(PropertyInfo property)
|
||||
{
|
||||
if (property == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(property));
|
||||
}
|
||||
|
||||
Property = property;
|
||||
Name = property.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the backing <see cref="PropertyInfo"/>.
|
||||
/// </summary>
|
||||
public PropertyInfo Property { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets (or sets in derived types) the property name.
|
||||
/// </summary>
|
||||
public virtual string Name { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property value getter.
|
||||
/// </summary>
|
||||
public Func<object, object> ValueGetter
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_valueGetter == null)
|
||||
{
|
||||
_valueGetter = MakeFastPropertyGetter(Property);
|
||||
}
|
||||
|
||||
return _valueGetter;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property value setter.
|
||||
/// </summary>
|
||||
public Action<object, object> ValueSetter
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_valueSetter == null)
|
||||
{
|
||||
_valueSetter = MakeFastPropertySetter(Property);
|
||||
}
|
||||
|
||||
return _valueSetter;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the property value for the specified <paramref name="instance"/>.
|
||||
/// </summary>
|
||||
/// <param name="instance">The object whose property value will be returned.</param>
|
||||
/// <returns>The property value.</returns>
|
||||
public object GetValue(object instance)
|
||||
{
|
||||
return ValueGetter(instance);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the property value for the specified <paramref name="instance" />.
|
||||
/// </summary>
|
||||
/// <param name="instance">The object whose property value will be set.</param>
|
||||
/// <param name="value">The property value.</param>
|
||||
public void SetValue(object instance, object value)
|
||||
{
|
||||
ValueSetter(instance, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and caches fast property helpers that expose getters for every public get property on the
|
||||
/// underlying type.
|
||||
/// </summary>
|
||||
/// <param name="typeInfo">The type info to extract property accessors for.</param>
|
||||
/// <returns>A cached array of all public properties of the specified type.
|
||||
/// </returns>
|
||||
public static PropertyHelper[] GetProperties(TypeInfo typeInfo)
|
||||
{
|
||||
return GetProperties(typeInfo.AsType());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates and caches fast property helpers that expose getters for every public get property on the
|
||||
/// specified type.
|
||||
/// </summary>
|
||||
/// <param name="type">The type to extract property accessors for.</param>
|
||||
/// <returns>A cached array of all public properties of the specified type.
|
||||
/// </returns>
|
||||
public static PropertyHelper[] GetProperties(Type type)
|
||||
{
|
||||
return GetProperties(type, CreateInstance, PropertiesCache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Creates and caches fast property helpers that expose getters for every non-hidden get property
|
||||
/// on the specified type.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <see cref="M:GetVisibleProperties"/> excludes properties defined on base types that have been
|
||||
/// hidden by definitions using the <c>new</c> keyword.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="typeInfo">The type info to extract property accessors for.</param>
|
||||
/// <returns>
|
||||
/// A cached array of all public properties of the specified type.
|
||||
/// </returns>
|
||||
public static PropertyHelper[] GetVisibleProperties(TypeInfo typeInfo)
|
||||
{
|
||||
return GetVisibleProperties(typeInfo.AsType(), CreateInstance, PropertiesCache, VisiblePropertiesCache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para>
|
||||
/// Creates and caches fast property helpers that expose getters for every non-hidden get property
|
||||
/// on the specified type.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <see cref="M:GetVisibleProperties"/> excludes properties defined on base types that have been
|
||||
/// hidden by definitions using the <c>new</c> keyword.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="type">The type to extract property accessors for.</param>
|
||||
/// <returns>
|
||||
/// A cached array of all public properties of the specified type.
|
||||
/// </returns>
|
||||
public static PropertyHelper[] GetVisibleProperties(Type type)
|
||||
{
|
||||
return GetVisibleProperties(type, CreateInstance, PropertiesCache, VisiblePropertiesCache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a single fast property getter. The result is not cached.
|
||||
/// </summary>
|
||||
/// <param name="propertyInfo">propertyInfo to extract the getter for.</param>
|
||||
/// <returns>a fast getter.</returns>
|
||||
/// <remarks>
|
||||
/// This method is more memory efficient than a dynamically compiled lambda, and about the
|
||||
/// same speed.
|
||||
/// </remarks>
|
||||
public static Func<object, object> MakeFastPropertyGetter(PropertyInfo propertyInfo)
|
||||
{
|
||||
Debug.Assert(propertyInfo != null);
|
||||
|
||||
return MakeFastPropertyGetter(
|
||||
propertyInfo,
|
||||
CallPropertyGetterOpenGenericMethod,
|
||||
CallPropertyGetterByReferenceOpenGenericMethod);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a single fast property getter which is safe for a null input object. The result is not cached.
|
||||
/// </summary>
|
||||
/// <param name="propertyInfo">propertyInfo to extract the getter for.</param>
|
||||
/// <returns>a fast getter.</returns>
|
||||
/// <remarks>
|
||||
/// This method is more memory efficient than a dynamically compiled lambda, and about the
|
||||
/// same speed.
|
||||
/// </remarks>
|
||||
public static Func<object, object> MakeNullSafeFastPropertyGetter(PropertyInfo propertyInfo)
|
||||
{
|
||||
Debug.Assert(propertyInfo != null);
|
||||
|
||||
return MakeFastPropertyGetter(
|
||||
propertyInfo,
|
||||
CallNullSafePropertyGetterOpenGenericMethod,
|
||||
CallNullSafePropertyGetterByReferenceOpenGenericMethod);
|
||||
}
|
||||
|
||||
private static Func<object, object> 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<object, object> 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<object, object>),
|
||||
propertyGetterDelegate);
|
||||
|
||||
return (Func<object, object>)accessorDelegate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a single fast property setter for reference types. The result is not cached.
|
||||
/// </summary>
|
||||
/// <param name="propertyInfo">propertyInfo to extract the setter for.</param>
|
||||
/// <returns>a fast getter.</returns>
|
||||
/// <remarks>
|
||||
/// This method is more memory efficient than a dynamically compiled lambda, and about the
|
||||
/// same speed. This only works for reference types.
|
||||
/// </remarks>
|
||||
public static Action<object, object> 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<object, object>), propertySetterAsAction);
|
||||
|
||||
return (Action<object, object>)callPropertySetterDelegate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 <see cref="IDictionary{String, Object}"/> instance, then a copy
|
||||
/// is returned.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The implementation of PropertyHelper will cache the property accessors per-type. This is
|
||||
/// faster when the same type is used multiple times with ObjectToDictionary.
|
||||
/// </remarks>
|
||||
public static IDictionary<string, object> ObjectToDictionary(object value)
|
||||
{
|
||||
var dictionary = value as IDictionary<string, object>;
|
||||
if (dictionary != null)
|
||||
{
|
||||
return new Dictionary<string, object>(dictionary, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
dictionary = new Dictionary<string, object>(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<TDeclaringType, TValue>(
|
||||
Func<TDeclaringType, TValue> getter,
|
||||
object target)
|
||||
{
|
||||
return getter((TDeclaringType)target);
|
||||
}
|
||||
|
||||
// Called via reflection
|
||||
private static object CallPropertyGetterByReference<TDeclaringType, TValue>(
|
||||
ByRefFunc<TDeclaringType, TValue> getter,
|
||||
object target)
|
||||
{
|
||||
var unboxed = (TDeclaringType)target;
|
||||
return getter(ref unboxed);
|
||||
}
|
||||
|
||||
// Called via reflection
|
||||
private static object CallNullSafePropertyGetter<TDeclaringType, TValue>(
|
||||
Func<TDeclaringType, TValue> getter,
|
||||
object target)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return getter((TDeclaringType)target);
|
||||
}
|
||||
|
||||
// Called via reflection
|
||||
private static object CallNullSafePropertyGetterByReference<TDeclaringType, TValue>(
|
||||
ByRefFunc<TDeclaringType, TValue> getter,
|
||||
object target)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var unboxed = (TDeclaringType)target;
|
||||
return getter(ref unboxed);
|
||||
}
|
||||
|
||||
private static void CallPropertySetter<TDeclaringType, TValue>(
|
||||
Action<TDeclaringType, TValue> setter,
|
||||
object target,
|
||||
object value)
|
||||
{
|
||||
setter((TDeclaringType)target, (TValue)value);
|
||||
}
|
||||
|
||||
protected static PropertyHelper[] GetVisibleProperties(
|
||||
Type type,
|
||||
Func<PropertyInfo, PropertyHelper> createPropertyHelper,
|
||||
ConcurrentDictionary<Type, PropertyHelper[]> allPropertiesCache,
|
||||
ConcurrentDictionary<Type, PropertyHelper[]> 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<PropertyHelper>(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<PropertyInfo, PropertyHelper> createPropertyHelper,
|
||||
ConcurrentDictionary<Type, PropertyHelper[]> cache)
|
||||
{
|
||||
// Unwrap nullable types. This means Nullable<T>.Value and Nullable<T>.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,6 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="FSharp.Core" />
|
||||
<Reference Include="System.Reflection.Metadata" />
|
||||
<Reference Include="System.Threading.Tasks.Extensions" />
|
||||
<Reference Include="System.Security.Cryptography.Cng" />
|
||||
|
|
|
|||
|
|
@ -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<int>(
|
||||
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<int>(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<int>.GetPropertiesToActivate(
|
||||
type: typeof(TestClass),
|
||||
activateAttributeType: typeof(TestActivateAttribute),
|
||||
createActivateInfo:
|
||||
(propertyInfo) => new PropertyActivator<int>(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<int>.GetPropertiesToActivate(
|
||||
type: typeof(TestClass),
|
||||
activateAttributeType: typeof(TestActivateAttribute),
|
||||
createActivateInfo:
|
||||
(propertyInfo) => new PropertyActivator<int>(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<int>.GetPropertiesToActivate(
|
||||
typeof(TestClassWithPropertyVisiblity),
|
||||
typeof(TestActivateAttribute),
|
||||
(propertyInfo) => new PropertyActivator<int>(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<int>.GetPropertiesToActivate(
|
||||
typeof(TestClassWithPropertyVisiblity),
|
||||
typeof(TestActivateAttribute),
|
||||
(propertyInfo) => new PropertyActivator<int>(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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<string>));
|
||||
|
||||
// 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<string, string>));
|
||||
|
||||
// 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<NullReferenceException>(() => 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<NullReferenceException>(() => 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<object, KeyValuePair<string, object>> IgnoreCaseTestData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<object, KeyValuePair<string, object>>
|
||||
{
|
||||
{
|
||||
new
|
||||
{
|
||||
selected = true,
|
||||
SeLeCtEd = false
|
||||
},
|
||||
new KeyValuePair<string, object>("selected", false)
|
||||
},
|
||||
{
|
||||
new
|
||||
{
|
||||
SeLeCtEd = false,
|
||||
selected = true
|
||||
},
|
||||
new KeyValuePair<string, object>("SeLeCtEd", true)
|
||||
},
|
||||
{
|
||||
new
|
||||
{
|
||||
SelECTeD = false,
|
||||
SeLECTED = true
|
||||
},
|
||||
new KeyValuePair<string, object>("SelECTeD", true)
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(IgnoreCaseTestData))]
|
||||
public void ObjectToDictionary_IgnoresPropertyCase(object testObject,
|
||||
KeyValuePair<string, object> 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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue