/// <summary> /// Gets the underlying element type. /// </summary> /// <param name="type">The type to extract the underlying type (if any).</param> /// <returns>The underlying element type.</returns> /// <returns>The underlying type.</returns> public static Type GetUnderlyingType(TypeInfo type) { if (type.IsArray) { return type.GetElementType(); } if (type.IsGenericType == false) { return type.AsType(); } Type enumerableType; if (TryGetEnumerableType(type, out enumerableType)) { return enumerableType.GetTypeInfo().GenericTypeArguments[0]; ; } return type.AsType(); }
/// <summary> /// Resolves <see cref="System.Type"/> to a <see cref="TypeSymbol"/> available in this assembly /// its referenced assemblies. /// </summary> /// <param name="type">The type to resolve.</param> /// <param name="includeReferences">Use referenced assemblies for resolution.</param> /// <returns>The resolved symbol if successful or null on failure.</returns> internal TypeSymbol GetTypeByReflectionType(Type type, bool includeReferences) { System.Reflection.TypeInfo typeInfo = type.GetTypeInfo(); Debug.Assert(!typeInfo.IsByRef); // not supported rigth now (we don't accept open types as submission results nor host types): Debug.Assert(!typeInfo.ContainsGenericParameters); if (typeInfo.IsArray) { TypeSymbol symbol = GetTypeByReflectionType(typeInfo.GetElementType(), includeReferences); if ((object)symbol == null) { return(null); } int rank = typeInfo.GetArrayRank(); return(new ArrayTypeSymbol(this, symbol, ImmutableArray <CustomModifier> .Empty, rank)); } else if (typeInfo.IsPointer) { TypeSymbol symbol = GetTypeByReflectionType(typeInfo.GetElementType(), includeReferences); if ((object)symbol == null) { return(null); } return(new PointerTypeSymbol(symbol)); } else if (typeInfo.DeclaringType != null) { Debug.Assert(!typeInfo.IsArray); // consolidated generic arguments (includes arguments of all declaring types): Type[] genericArguments = typeInfo.GenericTypeArguments; int typeArgumentIndex = 0; var currentTypeInfo = typeInfo.IsGenericType ? typeInfo.GetGenericTypeDefinition().GetTypeInfo() : typeInfo; var nestedTypes = ArrayBuilder <System.Reflection.TypeInfo> .GetInstance(); while (true) { Debug.Assert(currentTypeInfo.IsGenericTypeDefinition || !currentTypeInfo.IsGenericType); nestedTypes.Add(currentTypeInfo); if (currentTypeInfo.DeclaringType == null) { break; } currentTypeInfo = currentTypeInfo.DeclaringType.GetTypeInfo(); } int i = nestedTypes.Count - 1; var symbol = (NamedTypeSymbol)GetTypeByReflectionType(nestedTypes[i].AsType(), includeReferences); if ((object)symbol == null) { return(null); } while (--i >= 0) { int forcedArity = nestedTypes[i].GenericTypeParameters.Length - nestedTypes[i + 1].GenericTypeParameters.Length; MetadataTypeName mdName = MetadataTypeName.FromTypeName(nestedTypes[i].Name, forcedArity: forcedArity); symbol = symbol.LookupMetadataType(ref mdName); if ((object)symbol == null || symbol.IsErrorType()) { return(null); } symbol = ApplyGenericArguments(symbol, genericArguments, ref typeArgumentIndex, includeReferences); if ((object)symbol == null) { return(null); } } nestedTypes.Free(); Debug.Assert(typeArgumentIndex == genericArguments.Length); return(symbol); } else { AssemblyIdentity assemblyId = AssemblyIdentity.FromAssemblyDefinition(typeInfo.Assembly); MetadataTypeName mdName = MetadataTypeName.FromNamespaceAndTypeName( typeInfo.Namespace ?? string.Empty, typeInfo.Name, forcedArity: typeInfo.GenericTypeArguments.Length); NamedTypeSymbol symbol = GetTopLevelTypeByMetadataName(ref mdName, assemblyId, includeReferences, isWellKnownType: false); if ((object)symbol == null || symbol.IsErrorType()) { return(null); } int typeArgumentIndex = 0; Type[] genericArguments = typeInfo.GenericTypeArguments; symbol = ApplyGenericArguments(symbol, genericArguments, ref typeArgumentIndex, includeReferences); Debug.Assert(typeArgumentIndex == genericArguments.Length); return(symbol); } }
void SetupPart(System.Reflection.TypeInfo sourceType, BindingExpressionPart part) { part.Arguments = null; part.LastGetter = null; part.LastSetter = null; PropertyInfo property = null; if (part.IsIndexer) { if (sourceType.IsArray) { int index; if (!int.TryParse(part.Content, NumberStyles.Number, CultureInfo.InvariantCulture, out index)) { Console.WriteLine($"Binding : {part.Content} could not be parsed as an index for a {sourceType}"); } else { part.Arguments = new object[] { index } }; part.LastGetter = sourceType.GetDeclaredMethod("Get"); part.LastSetter = sourceType.GetDeclaredMethod("Set"); part.SetterType = sourceType.GetElementType(); } DefaultMemberAttribute defaultMember = sourceType.GetCustomAttributes(typeof(DefaultMemberAttribute), true).OfType <DefaultMemberAttribute>().FirstOrDefault(); string indexerName = defaultMember != null ? defaultMember.MemberName : "Item"; part.IndexerName = indexerName; #if NETSTANDARD2_0 try { property = sourceType.GetDeclaredProperty(indexerName); } catch (AmbiguousMatchException) { // Get most derived instance of property foreach (var p in sourceType.GetProperties().Where(prop => prop.Name == indexerName)) { if (property == null || property.DeclaringType.IsAssignableFrom(property.DeclaringType)) { property = p; } } } #else property = sourceType.GetDeclaredProperty(indexerName); #endif if (property == null) //is the indexer defined on the base class? { property = sourceType.BaseType?.GetProperty(indexerName); } if (property == null) //is the indexer defined on implemented interface ? { foreach (var implementedInterface in sourceType.ImplementedInterfaces) { property = implementedInterface.GetProperty(indexerName); if (property != null) { break; } } } if (property != null) { ParameterInfo parameter = property.GetIndexParameters().FirstOrDefault(); if (parameter != null) { try { object arg = Convert.ChangeType(part.Content, parameter.ParameterType, CultureInfo.InvariantCulture); part.Arguments = new[] { arg }; } catch (FormatException) { } catch (InvalidCastException) { } catch (OverflowException) { } } } } else { property = sourceType.GetDeclaredProperty(part.Content) ?? sourceType.BaseType?.GetProperty(part.Content); } if (property != null) { if (property.CanRead && property.GetMethod != null) { if (property.GetMethod.IsPublic && !property.GetMethod.IsStatic) { part.LastGetter = property.GetMethod; } } if (property.CanWrite && property.SetMethod != null) { if (property.SetMethod.IsPublic && !property.SetMethod.IsStatic) { part.LastSetter = property.SetMethod; part.SetterType = part.LastSetter.GetParameters().Last().ParameterType; if (Binding.AllowChaining) { FieldInfo bindablePropertyField = sourceType.GetDeclaredField(part.Content + "Property"); if (bindablePropertyField != null && bindablePropertyField.FieldType == typeof(BindableProperty) && sourceType.ImplementedInterfaces.Contains(typeof(IElementController))) { MethodInfo setValueMethod = null; #if NETSTANDARD1_0 foreach (MethodInfo m in sourceType.AsType().GetRuntimeMethods()) { if (m.Name.EndsWith("IElementController.SetValueFromRenderer")) { ParameterInfo[] parameters = m.GetParameters(); if (parameters.Length == 2 && parameters[0].ParameterType == typeof(BindableProperty)) { setValueMethod = m; break; } } } #else setValueMethod = typeof(IElementController).GetMethod("SetValueFromRenderer", new[] { typeof(BindableProperty), typeof(object) }); #endif if (setValueMethod != null) { part.LastSetter = setValueMethod; part.IsBindablePropertySetter = true; part.BindablePropertyField = bindablePropertyField.GetValue(null); } } } } } #if !NETSTANDARD1_0 //TupleElementNamesAttribute tupleEltNames; //if (property != null // && part.NextPart != null // && property.PropertyType.IsGenericType // && (property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<>) // || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,>) // || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,,>) // || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,,,>) // || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,,,,>) // || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,,,,,>) // || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,,,,,,>) // || property.PropertyType.GetGenericTypeDefinition() == typeof(ValueTuple<,,,,,,,>)) // && (tupleEltNames = property.GetCustomAttribute(typeof(TupleElementNamesAttribute)) as TupleElementNamesAttribute) != null) //{ // // modify the nextPart to access the tuple item via the ITuple indexer // var nextPart = part.NextPart; // var name = nextPart.Content; // var index = tupleEltNames.TransformNames.IndexOf(name); // if (index >= 0) // { // nextPart.IsIndexer = true; // nextPart.Content = index.ToString(); // } //} #endif } }
private static CreateMethod CreateObjectActivator(TypeInfo typeInfo, out object firstResult) { CreateMethod activator; firstResult = null; // Filter out non-instantiatable Types if (typeInfo.IsAbstract || typeInfo.IsInterface || typeInfo.IsGenericTypeDefinition) { activator = nullObjectActivator; } // If the caller wants a string, just return an empty one else if (typeInfo.AsType() == typeof(string)) { activator = () => ""; } // If the caller wants an array, create an empty one else if (typeInfo.IsArray && typeInfo.GetArrayRank() == 1) { activator = () => Array.CreateInstance(typeInfo.GetElementType(), 0); } // For structs, boxing a default(T) is sufficient else if (typeInfo.IsValueType) { var lambda = Expression.Lambda<CreateMethod>(Expression.Convert(Expression.Default(typeInfo.AsType()), typeof(object))); activator = lambda.Compile(); } else { activator = nullObjectActivator; // Retrieve constructors, sorted from trivial to parameter-rich ConstructorInfo[] constructors = typeInfo.DeclaredConstructors .Where(c => !c.IsStatic) .Select(c => new { Info = c, ParamCount = c.GetParameters().Length }) .OrderBy(s => s.ParamCount) .Select(s => s.Info) .ToArray(); Exception lastError = null; foreach (ConstructorInfo con in constructors) { // Prepare constructor argument values - just use default(T) for all of them. ParameterInfo[] conParams = con.GetParameters(); Expression[] args = new Expression[conParams.Length]; for (int i = 0; i < args.Length; i++) { Type paramType = conParams[i].ParameterType; args[i] = Expression.Default(paramType); } // Compile a lambda method invoking the constructor var lambda = Expression.Lambda<CreateMethod>(Expression.New(con, args)); activator = lambda.Compile(); // Does it work? firstResult = CheckActivator(activator, out lastError); if (firstResult != null) break; } // If all constructors failed, inform someone. This is not ideal. if (firstResult == null) { Log.Core.WriteWarning("Failed to create object of Type {0}. Make sure there is a trivial constructor.", Log.Type(typeInfo)); } } // Test whether our activation method really works, replace with dummy if not if (firstResult == null) { Exception error; firstResult = CheckActivator(activator, out error); // If we fail to initialize the Type due to a problem in its static constructor, it's likely a user problem. Let him know. if (error is TypeInitializationException) { Log.Core.WriteError("Failed to initialize Type {0}: {1}", Log.Type(typeInfo), Log.Exception(error.InnerException)); } } // If we still don't have anything, just use a dummy. if (firstResult == null) activator = nullObjectActivator; return activator; }
// Method to compare two types pointers for type equality // We cannot just compare the pointers as there can be duplicate type instances // for cloned and constructed types. static bool AreTypesEquivalentInternal(TypeInfo pType1, TypeInfo pType2) { if (!pType1.IsInstantiatedTypeInfo() && !pType2.IsInstantiatedTypeInfo()) return pType1.Equals(pType2); if (pType1.IsGenericType && pType2.IsGenericType) { if (!pType1.GetGenericTypeDefinition().Equals(pType2.GetGenericTypeDefinition())) return false; Type[] args1 = pType1.GenericTypeArguments; Type[] args2 = pType2.GenericTypeArguments; Debug.Assert(args1.Length == args2.Length); for (int i = 0; i < args1.Length; i++) { if (!AreTypesEquivalentInternal(args1[i].GetTypeInfo(), args2[i].GetTypeInfo())) return false; } return true; } if (pType1.IsArray && pType2.IsArray) { if (pType1.GetArrayRank() != pType2.GetArrayRank()) return false; return AreTypesEquivalentInternal(pType1.GetElementType().GetTypeInfo(), pType2.GetElementType().GetTypeInfo()); } if (pType1.IsPointer && pType2.IsPointer) { return AreTypesEquivalentInternal(pType1.GetElementType().GetTypeInfo(), pType2.GetElementType().GetTypeInfo()); } return false; }
// Internally callable version of the export method above. Has two additional parameters: // fBoxedSource : assume the source type is boxed so that value types and enums are // compatible with Object, ValueType and Enum (if applicable) // fAllowSizeEquivalence : allow identically sized integral types and enums to be considered // equivalent (currently used only for array element types) static bool AreTypesAssignableInternal(TypeInfo pSourceType, TypeInfo pTargetType, bool fBoxedSource, bool fAllowSizeEquivalence) { // // Are the types identical? // if (AreTypesEquivalentInternal(pSourceType, pTargetType)) return true; // // Handle cast to interface cases. // if (pTargetType.IsInterface) { // Value types can only be cast to interfaces if they're boxed. if (!fBoxedSource && pSourceType.IsValueType) return false; if (ImplementsInterface(pSourceType, pTargetType)) return true; // Are the types compatible due to generic variance? // if (pTargetType.HasGenericVariance && pSourceType.HasGenericVariance) if (pTargetType.IsGenericType && pSourceType.IsGenericType) return TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType); return false; } if (pSourceType.IsInterface) { // The only non-interface type an interface can be cast to is Object. return pTargetType.IsSystemObject(); } // // Handle cast to array cases. // if (pTargetType.IsArray) { if (pSourceType.IsArray) { if (pSourceType.GetElementType().GetTypeInfo().IsPointer) { // If the element types are pointers, then only exact matches are correct. // As we've already called AreTypesEquivalent at the start of this function, // return false as the exact match case has already been handled. // int** is not compatible with uint**, nor is int*[] oompatible with uint*[]. return false; } else { // Source type is also a pointer. Are the element types compatible? Note that using // AreTypesAssignableInternal here handles array covariance as well as IFoo[] . Foo[] // etc. Pass false for fBoxedSource since int[] is not assignable to object[]. return AreTypesAssignableInternal(pSourceType.GetElementType().GetTypeInfo(), pTargetType.GetElementType().GetTypeInfo(), false, true); } } // Can't cast a non-array type to an array. return false; } if (pSourceType.IsArray) { // Target type is not an array. But we can still cast arrays to Object or System.Array. return pTargetType.IsSystemObject() || pTargetType.IsSystemArray(); } // // Handle pointer cases // if (pTargetType.IsPointer) { if (pSourceType.IsPointer) { if (pSourceType.GetElementType().GetTypeInfo().IsPointer) { // If the element types are pointers, then only exact matches are correct. // As we've already called AreTypesEquivalent at the start of this function, // return false as the exact match case has already been handled. // int** is not compatible with uint**, nor is int*[] compatible with uint*[]. return false; } else { // Source type is also a pointer. Are the element types compatible? Note that using // AreTypesAssignableInternal here handles array covariance as well as IFoo[] . Foo[] // etc. Pass false for fBoxedSource since int[] is not assignable to object[]. return AreTypesAssignableInternal(pSourceType.GetElementType().GetTypeInfo(), pTargetType.GetElementType().GetTypeInfo(), false, true); } } return false; } else if (pSourceType.IsPointer) { return false; } // // Handle cast to other (non-interface, non-array) cases. // if (pSourceType.IsValueType) { // Certain value types of the same size are treated as equivalent when the comparison is // between array element types (indicated by fAllowSizeEquivalence). These are integer types // of the same size (e.g. int and uint) and the base type of enums vs all integer types of the // same size. if (fAllowSizeEquivalence && pTargetType.IsValueType) { if (ArePrimitveTypesEquivalentSize(pSourceType, pTargetType)) return true; // Non-identical value types aren't equivalent in any other case (since value types are // sealed). return false; } // If the source type is a value type but it's not boxed then we've run out of options: the types // are not identical, the target type isn't an interface and we're not allowed to check whether // the target type is a parent of this one since value types are sealed and thus the only matches // would be against Object, ValueType or Enum, all of which are reference types and not compatible // with non-boxed value types. if (!fBoxedSource) return false; } // // Are the types compatible via generic variance? // // if (pTargetType.HasGenericVariance && pSourceType.HasGenericVariance) if (pTargetType.IsGenericType && pSourceType.IsGenericType) { if (TypesAreCompatibleViaGenericVariance(pSourceType, pTargetType)) return true; } // Is the source type derived from the target type? if (IsDerived(pSourceType, pTargetType)) return true; return false; }
protected override string GetTypeName (TypeInfo type) { if (type.IsGenericType) { return base.GetTypeName(type) + "<" + string.Join (", ", type.GenericTypeArguments.Select (p => GetTypeName (p))) + ">"; } if (type.IsArray && type.HasElementType && type.GetElementType().GetTypeInfo().IsGenericType) { return GetTypeName (type.GetElementType ()) + string.Join("", Enumerable.Range (0, type.GetArrayRank ()).Select (_ => "[]")); } return base.GetTypeName (type).TrimEnd ('&'); }
private static bool CanCastTo(this TypeInfo fromTypeInfo, TypeInfo toTypeInfo, FoundationTypes foundationTypes) { if (fromTypeInfo.Equals(toTypeInfo)) return true; if (fromTypeInfo.IsArray) { if (toTypeInfo.IsInterface) return fromTypeInfo.CanCastArrayToInterface(toTypeInfo, foundationTypes); Type toType = toTypeInfo.AsType(); if (fromTypeInfo.IsSubclassOf(toType)) return true; // T[] is castable to Array or Object. if (!toTypeInfo.IsArray) return false; int rank = fromTypeInfo.GetArrayRank(); if (rank != toTypeInfo.GetArrayRank()) return false; bool fromTypeIsSzArray = fromTypeInfo.IsSzArray(foundationTypes); bool toTypeIsSzArray = toTypeInfo.IsSzArray(foundationTypes); if (fromTypeIsSzArray != toTypeIsSzArray) { // T[] is assignable to T[*] but not vice-versa. if (!(rank == 1 && !toTypeIsSzArray)) { return false; // T[*] is not castable to T[] } } TypeInfo toElementTypeInfo = toTypeInfo.GetElementType().GetTypeInfo(); TypeInfo fromElementTypeInfo = fromTypeInfo.GetElementType().GetTypeInfo(); return fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo, foundationTypes); } if (fromTypeInfo.IsByRef) { if (!toTypeInfo.IsByRef) return false; TypeInfo toElementTypeInfo = toTypeInfo.GetElementType().GetTypeInfo(); TypeInfo fromElementTypeInfo = fromTypeInfo.GetElementType().GetTypeInfo(); return fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo, foundationTypes); } if (fromTypeInfo.IsPointer) { Type toType = toTypeInfo.AsType(); if (toType.Equals(foundationTypes.SystemObject)) return true; // T* is castable to Object. if (toType.Equals(foundationTypes.SystemUIntPtr)) return true; // T* is castable to UIntPtr (but not IntPtr) if (!toTypeInfo.IsPointer) return false; TypeInfo toElementTypeInfo = toTypeInfo.GetElementType().GetTypeInfo(); TypeInfo fromElementTypeInfo = fromTypeInfo.GetElementType().GetTypeInfo(); return fromElementTypeInfo.IsElementTypeCompatibleWith(toElementTypeInfo, foundationTypes); } if (fromTypeInfo.IsGenericParameter) { // // A generic parameter can be cast to any of its constraints, or object, if none are specified, or ValueType if the "struct" constraint is // specified. // // This has to be coded as its own case as TypeInfo.BaseType on a generic parameter doesn't always return what you'd expect. // Type toType = toTypeInfo.AsType(); if (toType.Equals(foundationTypes.SystemObject)) return true; if (toType.Equals(foundationTypes.SystemValueType)) { GenericParameterAttributes attributes = fromTypeInfo.GenericParameterAttributes; if ((attributes & GenericParameterAttributes.NotNullableValueTypeConstraint) != 0) return true; } foreach (Type constraintType in fromTypeInfo.GetGenericParameterConstraints()) { if (constraintType.GetTypeInfo().CanCastTo(toTypeInfo, foundationTypes)) return true; } return false; } if (toTypeInfo.IsArray || toTypeInfo.IsByRef || toTypeInfo.IsPointer || toTypeInfo.IsGenericParameter) return false; if (fromTypeInfo.MatchesWithVariance(toTypeInfo, foundationTypes)) return true; if (toTypeInfo.IsInterface) { foreach (Type ifc in fromTypeInfo.ImplementedInterfaces) { if (ifc.GetTypeInfo().MatchesWithVariance(toTypeInfo, foundationTypes)) return true; } return false; } else { // Interfaces are always castable to System.Object. The code below will not catch this as interfaces report their BaseType as null. if (toTypeInfo.AsType().Equals(foundationTypes.SystemObject) && fromTypeInfo.IsInterface) return true; TypeInfo walk = fromTypeInfo; for (;;) { Type baseType = walk.BaseType; if (baseType == null) return false; walk = baseType.GetTypeInfo(); if (walk.MatchesWithVariance(toTypeInfo, foundationTypes)) return true; } } }
void SetupPart(TypeInfo sourceType, BindingExpressionPart part) { part.Arguments = null; part.LastGetter = null; part.LastSetter = null; PropertyInfo property = null; if (part.IsIndexer) { if (sourceType.IsArray) { int index; if (!int.TryParse(part.Content, out index)) Log.Warning("Binding", "{0} could not be parsed as an index for a {1}", part.Content, sourceType); else part.Arguments = new object[] { index }; part.LastGetter = sourceType.GetDeclaredMethod("Get"); part.LastSetter = sourceType.GetDeclaredMethod("Set"); part.SetterType = sourceType.GetElementType(); } DefaultMemberAttribute defaultMember = sourceType.GetCustomAttributes(typeof(DefaultMemberAttribute), true).OfType<DefaultMemberAttribute>().FirstOrDefault(); string indexerName = defaultMember != null ? defaultMember.MemberName : "Item"; part.IndexerName = indexerName; property = sourceType.GetDeclaredProperty(indexerName); if (property == null) property = sourceType.BaseType.GetProperty(indexerName); if (property != null) { ParameterInfo parameter = property.GetIndexParameters().FirstOrDefault(); if (parameter != null) { try { object arg = Convert.ChangeType(part.Content, parameter.ParameterType, CultureInfo.InvariantCulture); part.Arguments = new[] { arg }; } catch (FormatException) { } catch (InvalidCastException) { } catch (OverflowException) { } } } } else { property = sourceType.GetDeclaredProperty(part.Content); if (property == null) property = sourceType.BaseType.GetProperty(part.Content); } if (property != null) { if (property.CanRead && property.GetMethod.IsPublic && !property.GetMethod.IsStatic) part.LastGetter = property.GetMethod; if (property.CanWrite && property.SetMethod.IsPublic && !property.SetMethod.IsStatic) { part.LastSetter = property.SetMethod; part.SetterType = part.LastSetter.GetParameters().Last().ParameterType; if (Binding.AllowChaining) { FieldInfo bindablePropertyField = sourceType.GetDeclaredField(part.Content + "Property"); if (bindablePropertyField != null && bindablePropertyField.FieldType == typeof(BindableProperty) && sourceType.ImplementedInterfaces.Contains(typeof(IElementController))) { MethodInfo setValueMethod = null; foreach (MethodInfo m in sourceType.AsType().GetRuntimeMethods()) { if (m.Name.EndsWith("IElementController.SetValueFromRenderer")) { ParameterInfo[] parameters = m.GetParameters(); if (parameters.Length == 2 && parameters[0].ParameterType == typeof(BindableProperty)) { setValueMethod = m; break; } } } if (setValueMethod != null) { part.LastSetter = setValueMethod; part.IsBindablePropertySetter = true; part.BindablePropertyField = bindablePropertyField.GetValue(null); } } } } } }