183 lines
7.6 KiB
C#
183 lines
7.6 KiB
C#
|
//-----------------------------------------------------------------------------
|
|||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
|
|||
|
namespace System.Activities.Expressions
|
|||
|
{
|
|||
|
using System.ComponentModel;
|
|||
|
using System.Reflection;
|
|||
|
using System.Runtime;
|
|||
|
using System.Runtime.Serialization;
|
|||
|
using System.Threading;
|
|||
|
|
|||
|
public sealed class PropertyReference<TOperand, TResult> : CodeActivity<Location<TResult>>
|
|||
|
{
|
|||
|
PropertyInfo propertyInfo;
|
|||
|
Func<object, object[], object> getFunc;
|
|||
|
Func<object, object[], object> setFunc;
|
|||
|
MethodInfo getMethod;
|
|||
|
MethodInfo setMethod;
|
|||
|
|
|||
|
static MruCache<MethodInfo, Func<object, object[], object>> funcCache =
|
|||
|
new MruCache<MethodInfo, Func<object, object[], object>>(MethodCallExpressionHelper.FuncCacheCapacity);
|
|||
|
static ReaderWriterLockSlim locker = new ReaderWriterLockSlim();
|
|||
|
|
|||
|
[DefaultValue(null)]
|
|||
|
public string PropertyName
|
|||
|
{
|
|||
|
get;
|
|||
|
set;
|
|||
|
}
|
|||
|
|
|||
|
public InArgument<TOperand> Operand
|
|||
|
{
|
|||
|
get;
|
|||
|
set;
|
|||
|
}
|
|||
|
|
|||
|
protected override void CacheMetadata(CodeActivityMetadata metadata)
|
|||
|
{
|
|||
|
MethodInfo oldGetMethod = this.getMethod;
|
|||
|
MethodInfo oldSetMethod = this.setMethod;
|
|||
|
|
|||
|
bool isRequired = false;
|
|||
|
if (typeof(TOperand).IsEnum)
|
|||
|
{
|
|||
|
metadata.AddValidationError(SR.TargetTypeCannotBeEnum(this.GetType().Name, this.DisplayName));
|
|||
|
}
|
|||
|
else if (typeof(TOperand).IsValueType)
|
|||
|
{
|
|||
|
metadata.AddValidationError(SR.TargetTypeIsValueType(this.GetType().Name, this.DisplayName));
|
|||
|
}
|
|||
|
|
|||
|
if (string.IsNullOrEmpty(this.PropertyName))
|
|||
|
{
|
|||
|
metadata.AddValidationError(SR.ActivityPropertyMustBeSet("PropertyName", this.DisplayName));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Type operandType = typeof(TOperand);
|
|||
|
this.propertyInfo = operandType.GetProperty(this.PropertyName);
|
|||
|
|
|||
|
if (this.propertyInfo == null)
|
|||
|
{
|
|||
|
metadata.AddValidationError(SR.MemberNotFound(PropertyName, typeof(TOperand).Name));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
getMethod = this.propertyInfo.GetGetMethod();
|
|||
|
setMethod = this.propertyInfo.GetSetMethod();
|
|||
|
|
|||
|
// Only allow access to public properties, EXCEPT that Locations are top-level variables
|
|||
|
// from the other's perspective, not internal properties, so they're okay as a special case.
|
|||
|
// E.g. "[N]" from the user's perspective is not accessing a nonpublic property, even though
|
|||
|
// at an implementation level it is.
|
|||
|
if (setMethod == null && TypeHelper.AreTypesCompatible(this.propertyInfo.DeclaringType, typeof(Location)) == false)
|
|||
|
{
|
|||
|
metadata.AddValidationError(SR.ReadonlyPropertyCannotBeSet(this.propertyInfo.DeclaringType, this.propertyInfo.Name));
|
|||
|
}
|
|||
|
|
|||
|
if ((getMethod != null && !getMethod.IsStatic) || (setMethod != null && !setMethod.IsStatic))
|
|||
|
{
|
|||
|
isRequired = true;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
MemberExpressionHelper.AddOperandArgument(metadata, this.Operand, isRequired);
|
|||
|
if (propertyInfo != null)
|
|||
|
{
|
|||
|
if (MethodCallExpressionHelper.NeedRetrieve(this.getMethod, oldGetMethod, this.getFunc))
|
|||
|
{
|
|||
|
this.getFunc = MethodCallExpressionHelper.GetFunc(metadata, this.getMethod, funcCache, locker);
|
|||
|
}
|
|||
|
if (MethodCallExpressionHelper.NeedRetrieve(this.setMethod, oldSetMethod, this.setFunc))
|
|||
|
{
|
|||
|
this.setFunc = MethodCallExpressionHelper.GetFunc(metadata, this.setMethod, funcCache, locker);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
protected override Location<TResult> Execute(CodeActivityContext context)
|
|||
|
{
|
|||
|
Fx.Assert(this.propertyInfo != null, "propertyInfo must not be null");
|
|||
|
return new PropertyLocation<TResult>(this.propertyInfo, this.getFunc, this.setFunc, this.Operand.Get(context));
|
|||
|
}
|
|||
|
|
|||
|
[DataContract]
|
|||
|
internal class PropertyLocation<T> : Location<T>
|
|||
|
{
|
|||
|
object owner;
|
|||
|
|
|||
|
PropertyInfo propertyInfo;
|
|||
|
|
|||
|
Func<object, object[], object> getFunc;
|
|||
|
Func<object, object[], object> setFunc;
|
|||
|
|
|||
|
public PropertyLocation(PropertyInfo propertyInfo, Func<object, object[], object> getFunc,
|
|||
|
Func<object, object[], object> setFunc, object owner)
|
|||
|
: base()
|
|||
|
{
|
|||
|
this.propertyInfo = propertyInfo;
|
|||
|
this.owner = owner;
|
|||
|
this.getFunc = getFunc;
|
|||
|
this.setFunc = setFunc;
|
|||
|
}
|
|||
|
|
|||
|
public override T Value
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
// Only allow access to public properties, EXCEPT that Locations are top-level variables
|
|||
|
// from the other's perspective, not internal properties, so they're okay as a special case.
|
|||
|
// E.g. "[N]" from the user's perspective is not accessing a nonpublic property, even though
|
|||
|
// at an implementation level it is.
|
|||
|
if (this.getFunc != null)
|
|||
|
{
|
|||
|
if (!this.propertyInfo.GetGetMethod().IsStatic && this.owner == null)
|
|||
|
{
|
|||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.NullReferencedMemberAccess(this.propertyInfo.DeclaringType.Name, this.propertyInfo.Name)));
|
|||
|
}
|
|||
|
|
|||
|
return (T)this.getFunc(this.owner, new object[0]);
|
|||
|
}
|
|||
|
if (this.propertyInfo.GetGetMethod() == null && TypeHelper.AreTypesCompatible(this.propertyInfo.DeclaringType, typeof(Location)) == false)
|
|||
|
{
|
|||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.WriteonlyPropertyCannotBeRead(this.propertyInfo.DeclaringType, this.propertyInfo.Name)));
|
|||
|
}
|
|||
|
|
|||
|
return (T)this.propertyInfo.GetValue(this.owner, null);
|
|||
|
}
|
|||
|
set
|
|||
|
{
|
|||
|
if (this.setFunc != null)
|
|||
|
{
|
|||
|
if (!this.propertyInfo.GetSetMethod().IsStatic && this.owner == null)
|
|||
|
{
|
|||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.NullReferencedMemberAccess(this.propertyInfo.DeclaringType.Name, this.propertyInfo.Name)));
|
|||
|
}
|
|||
|
|
|||
|
this.setFunc(this.owner, new object[] { value });
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
this.propertyInfo.SetValue(this.owner, value, null);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[DataMember(EmitDefaultValue = false, Name = "owner")]
|
|||
|
internal object SerializedOwner
|
|||
|
{
|
|||
|
get { return this.owner; }
|
|||
|
set { this.owner = value; }
|
|||
|
}
|
|||
|
|
|||
|
[DataMember(Name = "propertyInfo")]
|
|||
|
internal PropertyInfo SerializedPropertyInfo
|
|||
|
{
|
|||
|
get { return this.propertyInfo; }
|
|||
|
set { this.propertyInfo = value; }
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|