public void TypeSpace_ArgumentChecks() { var ts = new TypeSpace(); AssertEx.ThrowsException <ArgumentNullException>(() => ts.GetMember(member: null), ex => Assert.AreEqual(ex.ParamName, "member")); AssertEx.ThrowsException <ArgumentNullException>(() => ts.GetConstructor(constructor: null), ex => Assert.AreEqual(ex.ParamName, "constructor")); AssertEx.ThrowsException <ArgumentNullException>(() => ts.GetField(field: null), ex => Assert.AreEqual(ex.ParamName, "field")); AssertEx.ThrowsException <ArgumentNullException>(() => ts.GetMethod(method: null), ex => Assert.AreEqual(ex.ParamName, "method")); AssertEx.ThrowsException <ArgumentNullException>(() => ts.GetProperty(property: null), ex => Assert.AreEqual(ex.ParamName, "property")); AssertEx.ThrowsException <ArgumentNullException>(() => ts.ConvertType(type: null), ex => Assert.AreEqual(ex.ParamName, "type")); AssertEx.ThrowsException <ArgumentNullException>(() => ts.MapType(type: null, typeSlim: null), ex => Assert.AreEqual(ex.ParamName, "type")); AssertEx.ThrowsException <ArgumentNullException>(() => ts.MapType(typeof(int), typeSlim: null), ex => Assert.AreEqual(ex.ParamName, "typeSlim")); }
/// <summary> /// Visits a member assignment slim tree node to produce a member assignment, /// taking implicit conversions for primitive data model types into account. /// </summary> /// <param name="node">Slim expression representation of the assignment.</param> /// <param name="expression">Expression assigned to the member, obtained from recursive conversion steps.</param> /// <returns>Member assignment node.</returns> protected override MemberAssignment MakeMemberAssignment(MemberAssignmentSlim node, Expression expression) { var member = TypeSpace.GetMember(node.Member); var lhsType = GetMemberAssignmentMemberType(member); var rhsType = expression.Type; // This case occurs when an enum is used in a known type and the underlying type of the enum // is used during anonymization. For example: // // xs.Select(x => new Person { Sex = Sex.Male, Age = x }) // // Erasure of the enum results in the following anonymized expression: // // xs.Select(x => new <>__Record { entity://person/sex = 1, entity://person/age = x }) // // Upon unification and reconstruction of the expression, the record type is unified against // the Person type, and an assignment incompatibility occurs for Sex = 1. // // A similar case exists during recursive rewriting of a subexpression whose type, declared // on a known type, is an enum. For example: // // xs.Select(x => new Person { Sex = x.Sex, Age = 21 }) // // Erasure of the enum results in the following anonymized expression: // // xs.Select(x => new <>__Record { entity://person/sex = x.entity://ape/sex, entity://person/age = 21 }) // // This time around, as the type of "x" gets resolved, the x.entity://ape/sex subexpression is // turned into x.Sex on the known type, which is an enum. Assignment to the record type's int // property now fails because of lack of conversion. if (lhsType != rhsType && (IsEnumAssignableToUnderlyingType(lhsType, rhsType) || IsEnumAssignableToUnderlyingType(rhsType, lhsType))) { expression = Expression.Convert(expression, lhsType); } return(base.MakeMemberAssignment(node, expression)); }
protected static MemberInfo MemberInfoRoundtrip(MemberInfo m, TypeSpace ts, InvertedTypeSpace its) { return(its.GetMember(ts.GetMember(m))); }