Click here to Skip to main content
15,879,535 members
Articles / Programming Languages / C#

Resolving Symbolic References in a CodeDOM (Part 7)

Rate me:
Please Sign up or sign in to vote.
4.75/5 (6 votes)
2 Dec 2012CDDL12 min read 19.4K   509   14  
Resolving symbolic references in a CodeDOM.
// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Mono.Cecil;
using Mono.Collections.Generic;
using Nova.CodeDOM;
using Nova.Utilities;

namespace Nova.Resolving
{
    /// <summary>
    /// Represents a possible match for an <see cref="UnresolvedRef"/>.
    /// </summary>
    public class MatchCandidate
    {
        #region /* FIELDS */

        /// <summary>
        /// The candidate <see cref="CodeObject"/> or <see cref="MemberInfo"/>.
        /// </summary>
        public readonly object Object;

        /// <summary>
        /// True if the <see cref="ResolveCategory"/> matches.
        /// </summary>
        public bool IsCategoryMatch;

        /// <summary>
        /// True if the type arguments match.
        /// </summary>
        public bool IsTypeArgumentsMatch = true;

        /// <summary>
        /// True if type inference failed.
        /// </summary>
        public bool IsTypeInferenceFailure;

        /// <summary>
        /// True if the method arguments match.
        /// </summary>
        public bool IsMethodArgumentsMatch = true;

        /// <summary>
        /// The index of the first non-matching method argument.
        /// </summary>
        public int MethodArgumentMismatchIndex = -1;

        /// <summary>
        /// True if the method arguments only failed to match because of <see cref="UnresolvedRef"/>s.
        /// </summary>
        public bool ArgumentsMismatchDueToUnresolvedOnly;

        /// <summary>
        /// True if the static modifiers match.
        /// </summary>
        public bool IsStaticModeMatch = true;

        /// <summary>
        /// True if the candidate is accessible.
        /// </summary>
        public bool IsAccessible = true;

        /// <summary>
        /// The associated <see cref="ResolveFlags"/>.
        /// </summary>
        public ResolveFlags ResolveFlags;

        /// <summary>
        /// Any inferred type arguments.
        /// </summary>
        public TypeRefBase[] InferredTypeArguments;

        /// <summary>
        /// An array of flags indicating if each inferred type argument has been fixed.
        /// </summary>
        public bool[] IsTypeArgumentFixed;

        /// <summary>
        /// True if the reference is inside a <see cref="DocCode"/> documentation comment.
        /// </summary>
        public bool IsDocCodeReference;

        #endregion

        #region /* CONSTRUCTORS */

        /// <summary>
        /// Construct a new MatchCandidate object.
        /// </summary>
        public MatchCandidate(object obj, UnresolvedRef unresolvedRef, ResolveFlags resolveFlags)
        {
            Object = obj;
            ResolveFlags = resolveFlags;

            if (!unresolvedRef.HasTypeArguments)
            {
                // If the reference doesn't have type arguments, but the matched object is a generic method,
                // then setup an inferred type argument list with OpenTypeParameterRef references to the declared
                // type parameters, which will then be replaced with actual types as they can be inferred.
                // If we're dealing with an explicit interface implementation, then use resolved TypeParameterRefs
                // instead of OpenTypeParameterRefs to the type parameters in the explicit interface method declaration.
                bool isExplicitInterfaceImplementation = unresolvedRef.IsExplicitInterfaceImplementation;
                if (obj is GenericMethodDecl)
                {
                    if (isExplicitInterfaceImplementation && unresolvedRef.Parent.Parent is GenericMethodDecl)
                        CopyTypeParameters((GenericMethodDecl)unresolvedRef.Parent.Parent, true);
                    else
                        CopyTypeParameters((GenericMethodDecl)obj, isExplicitInterfaceImplementation);
                }
                else if (obj is MethodDefinition && ((MethodDefinition)obj).HasGenericParameters)
                {
                    if (isExplicitInterfaceImplementation && unresolvedRef.Parent.Parent is GenericMethodDecl)
                        CopyTypeParameters((GenericMethodDecl)unresolvedRef.Parent.Parent, true);
                    else
                    {
                        Collection<GenericParameter> typeParameters = ((MethodDefinition)obj).GenericParameters;
                        InferredTypeArguments = new TypeRefBase[typeParameters.Count];
                        for (int i = 0; i < typeParameters.Count; ++i)
                            InferredTypeArguments[i] = (isExplicitInterfaceImplementation ? new TypeParameterRef(typeParameters[i]) : new OpenTypeParameterRef(typeParameters[i]));
                    }
                }
                else if (obj is MethodInfo && ((MethodInfo)obj).IsGenericMethod)
                {
                    if (isExplicitInterfaceImplementation && unresolvedRef.Parent.Parent is GenericMethodDecl)
                        CopyTypeParameters((GenericMethodDecl)unresolvedRef.Parent.Parent, true);
                    else
                    {
                        Type[] typeParameters = ((MethodInfo)obj).GetGenericArguments();
                        InferredTypeArguments = new TypeRefBase[typeParameters.Length];
                        for (int i = 0; i < typeParameters.Length; ++i)
                            InferredTypeArguments[i] = (isExplicitInterfaceImplementation ? new TypeParameterRef(typeParameters[i]) : new OpenTypeParameterRef(typeParameters[i]));
                    }
                }
                if (InferredTypeArguments != null)
                {
                    IsTypeArgumentFixed = new bool[InferredTypeArguments.Length];

                    // Mark the inferred types as already fixed if we're dealing with an explicit interface implementation
                    if (isExplicitInterfaceImplementation)
                        FixAllTypeArguments();
                }
            }
            else
            {
                // If the reference does have type arguments, and it's in a DocCodeRefBase, then "fake" inferred type arguments using the type
                // parameters from the declaration, if they match (in count and name).
                if (resolveFlags.HasFlag(ResolveFlags.InDocCodeRef) && unresolvedRef.IsDocCodeReference)
                {
                    IsDocCodeReference = true;
                    ChildList<Expression> typeArguments = unresolvedRef.TypeArguments;
                    if (obj is ITypeParameters)
                    {
                        ChildList<TypeParameter> typeParameters = ((ITypeParameters)obj).TypeParameters;
                        if (typeParameters != null && typeParameters.Count == typeArguments.Count && typeArguments[0] != null)
                        {
                            InferredTypeArguments = new TypeRefBase[typeParameters.Count];
                            for (int i = 0; i < typeParameters.Count; ++i)
                            {
                                Expression typeArgument = typeArguments[i];
                                if (typeArgument != null)
                                {
                                    InferredTypeArguments[i] = (typeArgument is UnresolvedRef && ((UnresolvedRef)typeArgument).Name == typeParameters[i].Name
                                        ? new TypeParameterRef(typeParameters[i]) : (TypeRefBase)typeArgument.Clone());
                                }
                            }
                        }
                    }
                    else if (obj is MethodDefinition)
                    {
                        if (((MethodDefinition)obj).HasGenericParameters)
                            CopyTypeParameters(((MethodDefinition)obj).GenericParameters, typeArguments);
                    }
                    else if (obj is TypeDefinition)
                    {
                        if (((TypeDefinition)obj).HasGenericParameters)
                            CopyTypeParameters(((TypeDefinition)obj).GenericParameters, typeArguments);
                    }
                    else if (obj is MethodInfo)
                    {
                        if (((MethodInfo)obj).IsGenericMethod)
                            CopyTypeParameters(((MethodInfo)obj).GetGenericArguments(), typeArguments);
                    }
                    else if (obj is Type)
                    {
                        if (((Type)obj).IsGenericType)
                            CopyTypeParameters(((Type)obj).GetGenericArguments(), typeArguments);
                    }
                    if (InferredTypeArguments != null)
                    {
                        IsTypeArgumentFixed = new bool[InferredTypeArguments.Length];
                        FixAllTypeArguments();
                    }
                }
            }
        }

        private void CopyTypeParameters(ITypeParameters iTypeParameters, bool isExplicitInterfaceImplementation)
        {
            ChildList<TypeParameter> typeParameters = iTypeParameters.TypeParameters;
            InferredTypeArguments = new TypeRefBase[typeParameters.Count];
            for (int i = 0; i < typeParameters.Count; ++i)
                InferredTypeArguments[i] = (isExplicitInterfaceImplementation ? new TypeParameterRef(typeParameters[i]) : new OpenTypeParameterRef(typeParameters[i]));
        }

        private void CopyTypeParameters(Collection<GenericParameter> typeParameters, ChildList<Expression> typeArguments)
        {
            if (typeParameters.Count == typeArguments.Count && typeArguments[0] != null)
            {
                InferredTypeArguments = new TypeRefBase[typeParameters.Count];
                for (int i = 0; i < typeParameters.Count; ++i)
                {
                    TypeRefBase typeArgument = typeArguments[i].EvaluateType();
                    if (typeArgument != null)
                    {
                        TypeRefBase inferredTypeArgument;
                        if (typeArgument is UnresolvedRef && typeArgument.Name == typeParameters[i].Name)
                        {
                            inferredTypeArgument = new TypeParameterRef(typeParameters[i]);
                            inferredTypeArgument.SetLineCol(typeArgument);
                        }
                        else
                            inferredTypeArgument = (TypeRefBase)typeArgument.Clone();
                        InferredTypeArguments[i] = inferredTypeArgument;
                    }
                }
            }
        }

        private void CopyTypeParameters(Type[] typeParameters, ChildList<Expression> typeArguments)
        {
            if (typeParameters.Length == typeArguments.Count && typeArguments[0] != null)
            {
                InferredTypeArguments = new TypeRefBase[typeParameters.Length];
                for (int i = 0; i < typeParameters.Length; ++i)
                {
                    TypeRefBase typeArgument = typeArguments[i].EvaluateType();
                    if (typeArgument != null)
                    {
                        TypeRefBase inferredTypeArgument;
                        if (typeArgument is UnresolvedRef && typeArgument.Name == typeParameters[i].Name)
                        {
                            inferredTypeArgument = new TypeParameterRef(typeParameters[i]);
                            inferredTypeArgument.SetLineCol(typeArgument);
                        }
                        else
                            inferredTypeArgument = (TypeRefBase)typeArgument.Clone();
                        InferredTypeArguments[i] = inferredTypeArgument;
                    }
                }
            }
        }

        private void FixAllTypeArguments()
        {
            for (int i = 0; i < IsTypeArgumentFixed.Length; ++i)
                IsTypeArgumentFixed[i] = true;
        }

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// True if the object is a method (does NOT include constructors or destructors).
        /// </summary>
        public bool IsMethod
        {
            get { return (Object is MethodDecl || (Object is MethodDefinition && !((MethodDefinition)Object).IsConstructor) || Object is MethodInfo); }
        }

        /// <summary>
        /// True if the candidate is a complete match.
        /// </summary>
        public bool IsCompleteMatch
        {
            get { return (IsCategoryMatch && IsTypeArgumentsMatch && !IsTypeInferenceFailure && IsMethodArgumentsMatch && IsStaticModeMatch && IsAccessible); }
        }

        #endregion

        #region /* METHODS */

        #region /* TYPE INFERENCE */

        /// <summary>
        /// Perform Type Inference for omitted type arguments on a generic method invocation.
        /// </summary>
        public bool InferTypeArguments(ICollection parameters, List<Expression> arguments, UnresolvedRef unresolvedRef)
        {
            // If we got this far, we know we have at least one parameter (which is necessary in order to infer type arguments).

            // The spec says "If the supplied number of arguments is different than the number of parameters for the method,
            // inference immediately fails.", but this is WRONG - we need to handle 'params' parameters, where the number of
            // arguments is either greater than the number of parameters or one less than the number of parameters!
            int argumentCount = arguments.Count;
            if (argumentCount > parameters.Count)
            {
                // If the last parameter is a 'params', then allow more arguments than parameters, but truncate the count so
                // that the extra arguments are ignored.  In this case, the last argument must match the array element type.
                if (ParameterRef.ParameterIsParams(parameters, parameters.Count - 1) && argumentCount > parameters.Count)
                    argumentCount = parameters.Count;
                else
                    return false;
            }
            else if (argumentCount < parameters.Count)
            {
                // Allow fewer arguments than parameters if the excess parameter is a 'params'.
                if (!ParameterRef.ParameterIsParams(parameters, argumentCount))
                    return false;
            }

            // If no unfixed type parameters exist then type inference succeeds (this can occur for explicit interface implementations,
            // which start out with inferred types that are already fixed).
            if (Enumerable.All(IsTypeArgumentFixed, delegate(bool isFixed) { return isFixed; }))
                return true;

            // Determine each parameter type P and any ref/out/params state, including evaluation of any type arguments
            TypeRefBase[] Parray = new TypeRefBase[argumentCount];
            bool[] PisRefOrOut = new bool[argumentCount];
            bool[] PisParams = new bool[argumentCount];
            GetParameterTypes(parameters, argumentCount, unresolvedRef, Parray, PisRefOrOut, PisParams);

            // If a 'params' parameter is being used in expanded form, convert it to a non-array non-params.
            // We know it has to be expanded form if arguments were truncated above, or if the type of the
            // argument isn't an array or it IS an array but it has fewer array ranks than the argument.
            if (PisParams[argumentCount - 1])
            {
                bool isExpanded = (argumentCount < arguments.Count);
                if (!isExpanded)
                {
                    TypeRefBase lastArgumentType = arguments[argumentCount - 1].EvaluateType();
                    if (lastArgumentType != null && (!lastArgumentType.IsArray || lastArgumentType.ArrayRanks.Count < Parray[argumentCount - 1].ArrayRanks.Count))
                        isExpanded = true;
                }
                if (isExpanded)
                {
                    Parray[argumentCount - 1] = Parray[argumentCount - 1].GetElementType();
                    PisParams[argumentCount - 1] = false;
                }
            }

            // Keep original type arguments around
            TypeRefBase[] originalTypeArguments = (TypeRefBase[])InferredTypeArguments.Clone();

            // For each of the method argument expressions E:
            for (int i = 0; i < argumentCount; ++i)
            {
                // If E is a null literal, an anonymous function, or a method group, nothing is inferred from the argument
                Expression E = arguments[i];
                if ((E is Literal && ((Literal)E).IsNull) || E is AnonymousMethod || (E is UnresolvedRef && ((UnresolvedRef)E).IsMethodGroup))
                    continue;

                // Evaluate the type of the argument expression as A, and get the parameter type as P
                TypeRefBase A = E.EvaluateType();
                TypeRefBase P = Parray[i];

                // The following steps are repeated as long as one is true:
                while (true)
                {
                    // - If P is an array type and A is an array type with the same rank, then replace A and P, respectively, with the element types of A and P.
                    // - If P is a type constructed from IEnumerable<T>, ICollection<T>, or IList<T> (all in the System.Collections.Generic namespace)
                    //   and A is a single-dimensional array type, then replace A and P, respectively, with the element types of A and P.
                    if (A.IsArray)
                    {
                        if (P.IsArray && P.ArrayRanks[0] == A.ArrayRanks[0])
                        {
                            P = P.GetElementType();
                            A = A.GetElementType();
                            continue;
                        }
                        if (A.ArrayRanks.Count == 1 && A.ArrayRanks[0] == 1 && P.HasTypeArguments
                            && (P.IsSameGenericType(TypeRef.IEnumerable1Ref) || P.IsSameGenericType(TypeRef.ICollection1Ref) || P.IsSameGenericType(TypeRef.IList1Ref)))
                        {
                            P = P.TypeArguments[0].EvaluateType();
                            A = A.GetElementType();
                            continue;
                        }
                    }
                    break;
                }

                // - If P is an array type (meaning that the previous step failed to relate A and P), then type inference fails for the generic method.
                if (P.IsArray)
                    return false;

                // - If P is a method type parameter, then type inference succeeds for this argument, and A is the type inferred for that type parameter.
                bool argumentSuccess = false;
                if (!InferTypeArgument(P, A, originalTypeArguments, ref argumentSuccess))
                    return false;
                if (argumentSuccess)
                    continue;

                // - Otherwise, P must be a constructed type. If, for each method type parameter MX that occurs in P, exactly one type TX can be determined
                // such that replacing each MX with each TX produces a type to which A is convertible by a standard implicit conversion, then inferencing
                // succeeds for this argument, and each TX is the type inferred for each MX. Method type parameter constraints, if any, are ignored for the
                // purpose of type inference. If, for a given MX, no TX exists or more than one TX exists, then type inference fails for the generic method
                // (a situation where more than one TX exists can only occur if P is a generic interface type and A implements multiple constructed versions
                // of that interface).
                if (P.HasTypeArguments)
                {
                    // Make a copy of P with the new TX type arguments so we can see if an implicit conversion is possible.
                    TypeRefBase P2 = (TypeRefBase)P.Clone();
                    bool foundTX = false;

                    // It's not obvious how to go about determining the TX types - so, we'll look for various specific cases.
                    // If A has the same number or more type arguments, try direct substitution (handles IEnumerable<T> <- List<Type>).
                    if (A.TypeArgumentCount >= P.TypeArgumentCount)
                    {
                        for (int j = 0; j < P2.TypeArguments.Count; ++j)
                            P2.TypeArguments[j] = (Expression)A.TypeArguments[j].Clone();
                        foundTX = true;
                    }
                    else if (P.TypeArgumentCount == 1)
                    {
                        // If P has a single type argument, and A is enumerable, try its element type (handles IEnumerable<T> <- MyCollection).
                        // Use the argument expression (E) so that any type parameters can be evaluated.
                        TypeRefBase elementTypeRef = ForEach.GetCollectionExpressionElementType(E);
                        if (elementTypeRef != null)
                        {
                            P2.TypeArguments[0] = elementTypeRef;
                            foundTX = true;
                        }
                    }

                    // If A is now implicitly convertible to P2, make an inference for each type argument
                    if (foundTX && A.IsImplicitlyConvertibleTo(P2, true))
                    {
                        for (int j = 0; j < P.TypeArgumentCount; ++j)
                        {
                            bool success = false;
                            if (!InferTypeArgument(P.TypeArguments[j].EvaluateType(), P2.TypeArguments[j].EvaluateType(), originalTypeArguments, ref success))
                                return false;
                        }
                    }
                }
            }

            // If no unfixed type parameters exist then type inference succeeds.
            return (Enumerable.All(IsTypeArgumentFixed, delegate(bool isFixed) { return isFixed; }));
        }

        protected bool InferTypeArgument(TypeRefBase P, TypeRefBase A, TypeRefBase[] originalTypeArguments, ref bool success)
        {
            for (int i = 0; i < InferredTypeArguments.Length; ++i)
            {
                // If P is an original type argument, infer A for the type argument, or if it's already been inferred,
                // type inference fails if A isn't implicitly convertiable to the previously inferred type.
                if (originalTypeArguments[i].IsSameRef(P))
                {
                    if (!IsTypeArgumentFixed[i])
                    {
                        InferredTypeArguments[i] = A;
                        IsTypeArgumentFixed[i] = success = true;
                        return true;
                    }
                    if (!A.IsImplicitlyConvertibleTo(InferredTypeArguments[i]))
                        return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Get the types of the specified collection of parameters, and also the ref/out and params status of each.
        /// </summary>
        protected void GetParameterTypes(ICollection parameters, int parameterCount, UnresolvedRef unresolvedRef, TypeRefBase[] T, bool[] TisRefOrOut, bool[] TisParams)
        {
            // To resolve any type arguments in the parameter types, a temporary MethodRef with the inferred type arguments
            // will be created to be used as the parent expression.  This method is only called during type inference, so we
            // know the UnresolvedRef represents a MethodRef with inferred type arguments.

            MethodRef parentMethodRef = (MethodRef)unresolvedRef.CreateRef(Object, false);
            parentMethodRef.Parent = unresolvedRef.Parent;
            CopyInferredTypeArguments(parentMethodRef);

            if (parameters is List<ParameterDecl>)
            {
                for (int i = 0; i < parameterCount; ++i)
                {
                    ParameterDecl parameterDecl = ((List<ParameterDecl>)parameters)[i];
                    TisParams[i] = parameterDecl.IsParams;
                    TisRefOrOut[i] = (parameterDecl.IsRef || parameterDecl.IsOut);
                    Expression typeExpression = parameterDecl.Type;
                    if (typeExpression != null)
                    {
                        TypeRefBase typeRefBase = typeExpression.EvaluateType();
                        if (typeRefBase != null)
                            T[i] = typeRefBase.EvaluateTypeArgumentTypes(parentMethodRef, parentMethodRef);
                    }
                }
            }
            else if (parameters is Collection<ParameterDefinition>)
            {
                for (int i = 0; i < parameterCount; ++i)
                {
                    ParameterDefinition parameterReference = ((Collection<ParameterDefinition>)parameters)[i];
                    TypeReference parameterType = parameterReference.ParameterType;
                    TisParams[i] = ParameterDefinitionUtil.IsParams(parameterReference);
                    TisRefOrOut[i] = parameterType.IsByReference;
                    // Dereference (remove the trailing '&') if it's a reference type
                    if (TisRefOrOut[i])
                        parameterType = ((ByReferenceType)parameterType).ElementType;
                    TypeRefBase typeRef = TypeRef.Create(parameterType);
                    T[i] = typeRef.EvaluateTypeArgumentTypes(parentMethodRef, parentMethodRef);
                }
            }
            else //if (parameters is ParameterInfo[])
            {
                for (int i = 0; i < parameterCount; ++i)
                {
                    ParameterInfo parameterInfo = ((ParameterInfo[])parameters)[i];
                    Type parameterType = parameterInfo.ParameterType;
                    TisParams[i] = ParameterInfoUtil.IsParams(parameterInfo);
                    TisRefOrOut[i] = parameterType.IsByRef;
                    // Dereference (remove the trailing '&') if it's a reference type
                    if (TisRefOrOut[i])
                        parameterType = parameterType.GetElementType();
                    TypeRef typeRef = TypeRef.Create(parameterType);
                    T[i] = typeRef.EvaluateTypeArgumentTypes(parentMethodRef, parentMethodRef);
                }
            }
        }

        #endregion

        /// <summary>
        /// Copy the inferred type arguments to the specified MethodRef or TypeRef (pseudo-inferred type arguments
        /// are used for references to type definitions in doc comments).
        /// </summary>
        public void CopyInferredTypeArguments(TypeRefBase typeRefBase)
        {
            // We must *clone* the type references while copying them, since we're adding them to the code object
            // tree, setting their parent references - we must not modify the existing type references.
            ChildList<Expression> typeArguments = new ChildList<Expression>(InferredTypeArguments.Length, typeRefBase);
            foreach (TypeRefBase inferredTypeRefBase in InferredTypeArguments)
                typeArguments.Add((TypeRefBase)inferredTypeRefBase.Clone());
            typeRefBase.TypeArguments = typeArguments;
            if (typeRefBase is MethodRef && !IsDocCodeReference)
                ((MethodRef)typeRefBase).HasInferredTypeArguments = true;
        }

        /// <summary>
        /// Determine if a mismatch is due to unresolved references only (as far as we can tell).
        /// </summary>
        public bool IsMismatchDueToUnresolvedOnly()
        {
            return (ArgumentsMismatchDueToUnresolvedOnly && IsTypeArgumentsMatch && !IsTypeInferenceFailure && IsStaticModeMatch && IsAccessible);
        }

        /// <summary>
        /// Determine if a mismatch is due to inaccessibility only.
        /// </summary>
        public bool IsMismatchDueToAccessibilityOnly()
        {
            return (!IsAccessible && IsCategoryMatch && IsMethodArgumentsMatch && IsTypeArgumentsMatch && !IsTypeInferenceFailure && IsStaticModeMatch);
        }

        /// <summary>
        /// Get a description of why the candidate doesn't match.
        /// </summary>
        public string GetMismatchDescription()
        {
            string result;
            bool isPlural = false;
            if (!IsCategoryMatch)
                result = "the category";
            else
            {
                result = "";
                if (!IsTypeArgumentsMatch)
                {
                    result += "the type arguments";
                    isPlural = true;
                }
                if (!IsMethodArgumentsMatch)
                {
                    result += (result.Length > 0 ? " and " : "") + "the method arguments";
                    isPlural = true;
                }
                if (!IsStaticModeMatch)
                    result += (result.Length > 0 ? " and " : "") + "the static mode";
            }
            if (!string.IsNullOrEmpty(result))
                result += (isPlural ? " don't" : " doesn't") + " match";
            if (IsTypeInferenceFailure)
                result += (result.Length > 0 ? " and " : "") + "type inference failed";
            if (!IsAccessible)
                result += (result.Length > 0 ? " and " : "") + "it isn't accessible";
            return result;
        }

        #endregion

        #region /* RENDERING */

        /// <summary>
        /// Render as a string (mainly for display in the debugger).
        /// </summary>
        public override string ToString()
        {
            return (IsCompleteMatch ? "Complete: " : "Partial: ") + Object;
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


Written By
Software Developer (Senior)
United States United States
I've been writing software since the late 70's, currently focusing mainly on C#.NET. I also like to travel around the world, and I own a Chocolate Factory (sadly, none of my employees are oompa loompas).

Comments and Discussions