CustomAttributeInfo
Encapsulates the information needed to build an attribute.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
namespace Castle.DynamicProxy
{
public class CustomAttributeInfo : IEquatable<CustomAttributeInfo>
{
private class AttributeArgumentValueEqualityComparer : IEqualityComparer<object>
{
bool IEqualityComparer<object>.Equals(object x, object y)
{
if (x == y)
return true;
if (x == null || y == null)
return false;
if (x.GetType() != y.GetType())
return false;
if (x.GetType().IsArray)
return AsObjectEnumerable(x).SequenceEqual(AsObjectEnumerable(y));
return x.Equals(y);
}
int IEqualityComparer<object>.GetHashCode(object obj)
{
if (obj == null)
return 0;
if (obj.GetType().IsArray)
return CombineHashCodes(AsObjectEnumerable(obj));
return obj.GetHashCode();
}
private static IEnumerable<object> AsObjectEnumerable(object array)
{
if (array.GetType().GetElementType().IsValueType)
return ((Array)array).Cast<object>();
return (IEnumerable<object>)array;
}
}
private static readonly PropertyInfo[] EmptyProperties = new PropertyInfo[0];
private static readonly FieldInfo[] EmptyFields = new FieldInfo[0];
private static readonly object[] EmptyValues = new object[0];
private static readonly IEqualityComparer<object> ValueComparer = new AttributeArgumentValueEqualityComparer();
private readonly CustomAttributeBuilder builder;
private readonly ConstructorInfo constructor;
private readonly object[] constructorArgs;
private readonly IDictionary<string, object> properties;
private readonly IDictionary<string, object> fields;
internal CustomAttributeBuilder Builder => builder;
public CustomAttributeInfo(ConstructorInfo constructor, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues, FieldInfo[] namedFields, object[] fieldValues)
{
builder = new CustomAttributeBuilder(constructor, constructorArgs, namedProperties, propertyValues, namedFields, fieldValues);
this.constructor = constructor;
this.constructorArgs = ((constructorArgs.Length == 0) ? EmptyValues : constructorArgs.ToArray());
properties = MakeNameValueDictionary(namedProperties, propertyValues);
fields = MakeNameValueDictionary(namedFields, fieldValues);
}
public CustomAttributeInfo(ConstructorInfo constructor, object[] constructorArgs, PropertyInfo[] namedProperties, object[] propertyValues)
: this(constructor, constructorArgs, namedProperties, propertyValues, EmptyFields, EmptyValues)
{
}
public CustomAttributeInfo(ConstructorInfo constructor, object[] constructorArgs, FieldInfo[] namedFields, object[] fieldValues)
: this(constructor, constructorArgs, EmptyProperties, EmptyValues, namedFields, fieldValues)
{
}
public CustomAttributeInfo(ConstructorInfo constructor, object[] constructorArgs)
: this(constructor, constructorArgs, EmptyProperties, EmptyValues, EmptyFields, EmptyValues)
{
}
public static CustomAttributeInfo FromExpression(Expression<Func<Attribute>> expression)
{
List<PropertyInfo> list = new List<PropertyInfo>();
List<object> list2 = new List<object>();
List<FieldInfo> list3 = new List<FieldInfo>();
List<object> list4 = new List<object>();
Expression expression2 = UnwrapBody(expression.Body);
NewExpression newExpression = expression2 as NewExpression;
if (newExpression == null) {
MemberInitExpression obj = expression2 as MemberInitExpression;
if (obj == null)
throw new ArgumentException("The expression must be either a simple constructor call or an object initializer expression");
newExpression = obj.NewExpression;
foreach (MemberBinding binding in obj.Bindings) {
MemberAssignment memberAssignment = binding as MemberAssignment;
if (memberAssignment == null)
throw new ArgumentException("Only assignment bindings are supported");
object attributeArgumentValue = GetAttributeArgumentValue(memberAssignment.Expression, true);
PropertyInfo propertyInfo = memberAssignment.Member as PropertyInfo;
if (propertyInfo != (PropertyInfo)null) {
list.Add(propertyInfo);
list2.Add(attributeArgumentValue);
} else {
FieldInfo fieldInfo = memberAssignment.Member as FieldInfo;
if (!(fieldInfo != (FieldInfo)null))
throw new ArgumentException("Only property and field assignments are supported");
list3.Add(fieldInfo);
list4.Add(attributeArgumentValue);
}
}
}
List<object> list5 = new List<object>();
foreach (Expression argument in newExpression.Arguments) {
object attributeArgumentValue2 = GetAttributeArgumentValue(argument, true);
list5.Add(attributeArgumentValue2);
}
return new CustomAttributeInfo(newExpression.Constructor, list5.ToArray(), list.ToArray(), list2.ToArray(), list3.ToArray(), list4.ToArray());
}
private static Expression UnwrapBody(Expression body)
{
UnaryExpression unaryExpression = body as UnaryExpression;
if (unaryExpression != null && unaryExpression.NodeType == ExpressionType.Convert)
return unaryExpression.Operand;
return body;
}
private static object GetAttributeArgumentValue(Expression arg, bool allowArray)
{
switch (arg.NodeType) {
case ExpressionType.Constant:
return ((ConstantExpression)arg).Value;
case ExpressionType.MemberAccess: {
MemberExpression memberExpression = (MemberExpression)arg;
FieldInfo fieldInfo = memberExpression.Member as FieldInfo;
if ((object)fieldInfo != null) {
ConstantExpression constantExpression = memberExpression.Expression as ConstantExpression;
if (constantExpression != null && IsCompilerGenerated(constantExpression.Type) && constantExpression.Value != null)
return fieldInfo.GetValue(constantExpression.Value);
}
break;
}
case ExpressionType.NewArrayInit:
if (allowArray) {
NewArrayExpression newArrayExpression = (NewArrayExpression)arg;
Array array = Array.CreateInstance(newArrayExpression.Type.GetElementType(), newArrayExpression.Expressions.Count);
int num = 0;
{
foreach (Expression expression in newArrayExpression.Expressions) {
object attributeArgumentValue = GetAttributeArgumentValue(expression, false);
array.SetValue(attributeArgumentValue, num);
num++;
}
return array;
}
}
break;
}
throw new ArgumentException("Only constant, local variables, method parameters and single-dimensional array expressions are supported");
}
private static bool IsCompilerGenerated(Type type)
{
return CustomAttributeExtensions.IsDefined(type, typeof(CompilerGeneratedAttribute));
}
public bool Equals(CustomAttributeInfo other)
{
if (other == null)
return false;
if (this == other)
return true;
if (constructor.Equals(other.constructor) && constructorArgs.SequenceEqual(other.constructorArgs, ValueComparer) && AreMembersEquivalent(properties, other.properties))
return AreMembersEquivalent(fields, other.fields);
return false;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
if (this == obj)
return true;
if (obj.GetType() != GetType())
return false;
return Equals((CustomAttributeInfo)obj);
}
public override int GetHashCode()
{
return (((((constructor.GetHashCode() * 397) ^ CombineHashCodes(constructorArgs)) * 397) ^ CombineMemberHashCodes(properties)) * 397) ^ CombineMemberHashCodes(fields);
}
private static bool AreMembersEquivalent(IDictionary<string, object> x, IDictionary<string, object> y)
{
if (x.Count != y.Count)
return false;
foreach (KeyValuePair<string, object> item in x) {
if (!y.TryGetValue(item.Key, out object value))
return false;
if (!ValueComparer.Equals(item.Value, value))
return false;
}
return true;
}
private static int CombineHashCodes(IEnumerable<object> values)
{
int num = 173;
foreach (object value in values) {
num = ((num * 397) ^ ValueComparer.GetHashCode(value));
}
return num;
}
private static int CombineMemberHashCodes(IDictionary<string, object> dict)
{
int num = 0;
foreach (KeyValuePair<string, object> item in dict) {
int hashCode = item.Key.GetHashCode();
int hashCode2 = ValueComparer.GetHashCode(item.Value);
num += ((hashCode * 397) ^ hashCode2);
}
return num;
}
private IDictionary<string, object> MakeNameValueDictionary<T>(T[] members, object[] values) where T : MemberInfo
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
for (int i = 0; i < members.Length; i++) {
dictionary.Add(members[i].Name, values[i]);
}
return dictionary;
}
}
}