• Asp.net MVC DefaultModelBinder分析


    今天看见一同事写了一段代码很是奇怪,大致结构如下:

     public ActionResult Demo(string name, dynamic obj)
            {
                if (obj != null)
                {
                    return Content("obj is not null");
                }
                return Content("obj is  null");
            }

    在调用action时obj参数不传如@{Html.RenderAction("Demo",new {name="majiang"});}

    可是在实际运行中obj永远不等于null。这是为什么了? 其实一切答案都在DefaultModelBinder

    首先看看BindModel方法

            public virtual object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
                if (bindingContext == null) {
                    throw new ArgumentNullException("bindingContext");
                }
    
                bool performedFallback = false;
    
                if (!String.IsNullOrEmpty(bindingContext.ModelName) && !bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName)) {
                    // We couldn't find any entry that began with the prefix. If this is the top-level element, fall back
                    // to the empty prefix.
                    if (bindingContext.FallbackToEmptyPrefix) {
                        bindingContext = new ModelBindingContext() {
                            ModelMetadata = bindingContext.ModelMetadata,
                            ModelState = bindingContext.ModelState,
                            PropertyFilter = bindingContext.PropertyFilter,
                            ValueProvider = bindingContext.ValueProvider
                        };
                        performedFallback = true;
                    }
                    else {
                        return null;
                    }
                }
    
                // Simple model = int, string, etc.; determined by calling TypeConverter.CanConvertFrom(typeof(string))
                // or by seeing if a value in the request exactly matches the name of the model we're binding.
                // Complex type = everything else.
                if (!performedFallback) {
                    bool performRequestValidation = ShouldPerformRequestValidation(controllerContext, bindingContext);
                    ValueProviderResult vpResult = bindingContext.UnvalidatedValueProvider.GetValue(bindingContext.ModelName, skipValidation: !performRequestValidation);
                    if (vpResult != null) {
                        return BindSimpleModel(controllerContext, bindingContext, vpResult);
                    }
                }
                if (!bindingContext.ModelMetadata.IsComplexType) {
                    return null;
                }
    
                return BindComplexModel(controllerContext, bindingContext);
            }

    很显然object是复杂类型 应该关注方法BindComplexModel

    internal object BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
                object model = bindingContext.Model;
                Type modelType = bindingContext.ModelType;
    
                // if we're being asked to create an array, create a list instead, then coerce to an array after the list is created
                if (model == null && modelType.IsArray) {
                    Type elementType = modelType.GetElementType();
                    Type listType = typeof(List<>).MakeGenericType(elementType);
                    object collection = CreateModel(controllerContext, bindingContext, listType);
    
                    ModelBindingContext arrayBindingContext = new ModelBindingContext() {
                        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => collection, listType),
                        ModelName = bindingContext.ModelName,
                        ModelState = bindingContext.ModelState,
                        PropertyFilter = bindingContext.PropertyFilter,
                        ValueProvider = bindingContext.ValueProvider
                    };
                    IList list = (IList)UpdateCollection(controllerContext, arrayBindingContext, elementType);
    
                    if (list == null) {
                        return null;
                    }
    
                    Array array = Array.CreateInstance(elementType, list.Count);
                    list.CopyTo(array, 0);
                    return array;
                }
    
                if (model == null) {
                    model = CreateModel(controllerContext, bindingContext, modelType);
                }
    
                // special-case IDictionary<,> and ICollection<>
                Type dictionaryType = TypeHelpers.ExtractGenericInterface(modelType, typeof(IDictionary<,>));
                if (dictionaryType != null) {
                    Type[] genericArguments = dictionaryType.GetGenericArguments();
                    Type keyType = genericArguments[0];
                    Type valueType = genericArguments[1];
    
                    ModelBindingContext dictionaryBindingContext = new ModelBindingContext() {
                        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, modelType),
                        ModelName = bindingContext.ModelName,
                        ModelState = bindingContext.ModelState,
                        PropertyFilter = bindingContext.PropertyFilter,
                        ValueProvider = bindingContext.ValueProvider
                    };
                    object dictionary = UpdateDictionary(controllerContext, dictionaryBindingContext, keyType, valueType);
                    return dictionary;
                }
    
                Type enumerableType = TypeHelpers.ExtractGenericInterface(modelType, typeof(IEnumerable<>));
                if (enumerableType != null) {
                    Type elementType = enumerableType.GetGenericArguments()[0];
    
                    Type collectionType = typeof(ICollection<>).MakeGenericType(elementType);
                    if (collectionType.IsInstanceOfType(model)) {
                        ModelBindingContext collectionBindingContext = new ModelBindingContext() {
                            ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, modelType),
                            ModelName = bindingContext.ModelName,
                            ModelState = bindingContext.ModelState,
                            PropertyFilter = bindingContext.PropertyFilter,
                            ValueProvider = bindingContext.ValueProvider
                        };
                        object collection = UpdateCollection(controllerContext, collectionBindingContext, elementType);
                        return collection;
                    }
                }
    
                // otherwise, just update the properties on the complex type
                BindComplexElementalModel(controllerContext, bindingContext, model);
                return model;
            }

    在它里面有关键的一句
    if (model == null) {
                    model = CreateModel(controllerContext, bindingContext, modelType);
                }

            protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
            {
                Type typeToCreate = modelType;
    
                // we can understand some collection interfaces, e.g. IList<>, IDictionary<,>
                if (modelType.IsGenericType)
                {
                    Type genericTypeDefinition = modelType.GetGenericTypeDefinition();
                    if (genericTypeDefinition == typeof(IDictionary<,>))
                    {
                        typeToCreate = typeof(Dictionary<,>).MakeGenericType(modelType.GetGenericArguments());
                    }
                    else if (genericTypeDefinition == typeof(IEnumerable<>) || genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IList<>))
                    {
                        typeToCreate = typeof(List<>).MakeGenericType(modelType.GetGenericArguments());
                    }
                }
    
                // fallback to the type's default constructor
                return Activator.CreateInstance(typeToCreate);
            }

    很明显 在绑定的时候发现复杂类型实例为null,这时DefaultModelBinder会创建一个默认实例。这就是为什么action参数为object的时候,再绑定后都不为null。然而string类型的数据他们走的是BindSimpleModel。

    为了便于大家调试代码,证实以上结论,大家可以创建一个类继承与DefaultModelBinder,如CustBinder

    然后再Application_Start() 中添加一句   ModelBinders.Binders.DefaultBinder = new CustBinder();

    CustBinder代码:

      public class CustBinder : DefaultModelBinder
        {
            public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
                //return base.BindModel(controllerContext, bindingContext);
                if (bindingContext == null)
                {
                    throw new ArgumentNullException("bindingContext");
                }
    
                bool performedFallback = false;
    
                if (!String.IsNullOrEmpty(bindingContext.ModelName) && !bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName))
                {
                    // We couldn't find any entry that began with the prefix. If this is the top-level element, fall back
                    // to the empty prefix.
                    if (bindingContext.FallbackToEmptyPrefix)
                    {
                        bindingContext = new ModelBindingContext()
                        {
                            ModelMetadata = bindingContext.ModelMetadata,
                            ModelState = bindingContext.ModelState,
                            PropertyFilter = bindingContext.PropertyFilter,
                            ValueProvider = bindingContext.ValueProvider
                        };
                        performedFallback = true;
                    }
                    else
                    {
                        return null;
                    }
                }
    
                // Simple model = int, string, etc.; determined by calling TypeConverter.CanConvertFrom(typeof(string))
                // or by seeing if a value in the request exactly matches the name of the model we're binding.
                // Complex type = everything else.
                if (!performedFallback)
                {
                    bool performRequestValidation = ShouldPerformRequestValidation(controllerContext, bindingContext);
    
                    //ValueProviderResult vpResult = bindingContext.UnvalidatedValueProvider.GetValue(bindingContext.ModelName, skipValidation: !performRequestValidation);
                    ValueProviderResult vpResult = (bindingContext.ValueProvider as IUnvalidatedValueProvider).GetValue(bindingContext.ModelName, skipValidation: !performRequestValidation);
                    if (vpResult != null)
                    {
                        return BindSimpleModel(controllerContext, bindingContext, vpResult);
                    }
                }
                if (!bindingContext.ModelMetadata.IsComplexType)
                {
                    return null;
                }
    
                return BindComplexModel(controllerContext, bindingContext);
            }
    
            protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
            {
                Type typeToCreate = modelType;
    
                // we can understand some collection interfaces, e.g. IList<>, IDictionary<,>
                if (modelType.IsGenericType)
                {
                    Type genericTypeDefinition = modelType.GetGenericTypeDefinition();
                    if (genericTypeDefinition == typeof(IDictionary<,>))
                    {
                        typeToCreate = typeof(Dictionary<,>).MakeGenericType(modelType.GetGenericArguments());
                    }
                    else if (genericTypeDefinition == typeof(IEnumerable<>) || genericTypeDefinition == typeof(ICollection<>) || genericTypeDefinition == typeof(IList<>))
                    {
                        typeToCreate = typeof(List<>).MakeGenericType(modelType.GetGenericArguments());
                    }
                }
    
                // fallback to the type's default constructor
                return Activator.CreateInstance(typeToCreate);
            }
    
            internal object BindSimpleModel(ControllerContext controllerContext, ModelBindingContext bindingContext, ValueProviderResult valueProviderResult)
            {
                bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
    
                // if the value provider returns an instance of the requested data type, we can just short-circuit
                // the evaluation and return that instance
                if (bindingContext.ModelType.IsInstanceOfType(valueProviderResult.RawValue))
                {
                    return valueProviderResult.RawValue;
                }
    
                // since a string is an IEnumerable<char>, we want it to skip the two checks immediately following
                if (bindingContext.ModelType != typeof(string))
                {
    
                    // conversion results in 3 cases, as below
                    if (bindingContext.ModelType.IsArray)
                    {
                        // case 1: user asked for an array
                        // ValueProviderResult.ConvertTo() understands array types, so pass in the array type directly
                        object modelArray = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType);
                        return modelArray;
                    }
    
                    Type enumerableType = TypeHelpers.ExtractGenericInterface(bindingContext.ModelType, typeof(IEnumerable<>));
                    if (enumerableType != null)
                    {
                        // case 2: user asked for a collection rather than an array
                        // need to call ConvertTo() on the array type, then copy the array to the collection
                        object modelCollection = CreateModel(controllerContext, bindingContext, bindingContext.ModelType);
                        Type elementType = enumerableType.GetGenericArguments()[0];
                        Type arrayType = elementType.MakeArrayType();
                        object modelArray = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, arrayType);
    
                        Type collectionType = typeof(ICollection<>).MakeGenericType(elementType);
                        if (collectionType.IsInstanceOfType(modelCollection))
                        {
                            CollectionHelpers.ReplaceCollection(elementType, modelCollection, modelArray);
                        }
                        return modelCollection;
                    }
                }
    
                // case 3: user asked for an individual element
                object model = ConvertProviderResult(bindingContext.ModelState, bindingContext.ModelName, valueProviderResult, bindingContext.ModelType);
                return model;
            }
    
            internal object BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
                object model = bindingContext.Model;
                Type modelType = bindingContext.ModelType;
    
                // if we're being asked to create an array, create a list instead, then coerce to an array after the list is created
                if (model == null && modelType.IsArray)
                {
                    Type elementType = modelType.GetElementType();
                    Type listType = typeof(List<>).MakeGenericType(elementType);
                    object collection = CreateModel(controllerContext, bindingContext, listType);
    
                    ModelBindingContext arrayBindingContext = new ModelBindingContext()
                    {
                        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => collection, listType),
                        ModelName = bindingContext.ModelName,
                        ModelState = bindingContext.ModelState,
                        PropertyFilter = bindingContext.PropertyFilter,
                        ValueProvider = bindingContext.ValueProvider
                    };
                    IList list = (IList)UpdateCollection(controllerContext, arrayBindingContext, elementType);
    
                    if (list == null)
                    {
                        return null;
                    }
    
                    Array array = Array.CreateInstance(elementType, list.Count);
                    list.CopyTo(array, 0);
                    return array;
                }
    
                if (model == null)
                {
                    model = CreateModel(controllerContext, bindingContext, modelType);
                }
    
                // special-case IDictionary<,> and ICollection<>
                Type dictionaryType = TypeHelpers.ExtractGenericInterface(modelType, typeof(IDictionary<,>));
                if (dictionaryType != null)
                {
                    Type[] genericArguments = dictionaryType.GetGenericArguments();
                    Type keyType = genericArguments[0];
                    Type valueType = genericArguments[1];
    
                    ModelBindingContext dictionaryBindingContext = new ModelBindingContext()
                    {
                        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, modelType),
                        ModelName = bindingContext.ModelName,
                        ModelState = bindingContext.ModelState,
                        PropertyFilter = bindingContext.PropertyFilter,
                        ValueProvider = bindingContext.ValueProvider
                    };
                    object dictionary = UpdateDictionary(controllerContext, dictionaryBindingContext, keyType, valueType);
                    return dictionary;
                }
    
                Type enumerableType = TypeHelpers.ExtractGenericInterface(modelType, typeof(IEnumerable<>));
                if (enumerableType != null)
                {
                    Type elementType = enumerableType.GetGenericArguments()[0];
    
                    Type collectionType = typeof(ICollection<>).MakeGenericType(elementType);
                    if (collectionType.IsInstanceOfType(model))
                    {
                        ModelBindingContext collectionBindingContext = new ModelBindingContext()
                        {
                            ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, modelType),
                            ModelName = bindingContext.ModelName,
                            ModelState = bindingContext.ModelState,
                            PropertyFilter = bindingContext.PropertyFilter,
                            ValueProvider = bindingContext.ValueProvider
                        };
                        object collection = UpdateCollection(controllerContext, collectionBindingContext, elementType);
                        return collection;
                    }
                }
    
                // otherwise, just update the properties on the complex type
                BindComplexElementalModel(controllerContext, bindingContext, model);
                return model;
            }
    
        
            internal void BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, object model)
            {
                // need to replace the property filter + model object and create an inner binding context
                ModelBindingContext newBindingContext = CreateComplexElementalModelBindingContext(controllerContext, bindingContext, model);
    
                // validation
                if (OnModelUpdating(controllerContext, newBindingContext))
                {
                    BindProperties(controllerContext, newBindingContext);
                    OnModelUpdated(controllerContext, newBindingContext);
                }
            }
           
       
            internal ModelBindingContext CreateComplexElementalModelBindingContext(ControllerContext controllerContext, ModelBindingContext bindingContext, object model)
            {
                BindAttribute bindAttr = (BindAttribute)GetTypeDescriptor(controllerContext, bindingContext).GetAttributes()[typeof(BindAttribute)];
                Predicate<string> newPropertyFilter = (bindAttr != null)
                    ? propertyName => bindAttr.IsPropertyAllowed(propertyName) && bindingContext.PropertyFilter(propertyName)
                    : bindingContext.PropertyFilter;
    
                ModelBindingContext newBindingContext = new ModelBindingContext()
                {
                    ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, bindingContext.ModelType),
                    ModelName = bindingContext.ModelName,
                    ModelState = bindingContext.ModelState,
                    PropertyFilter = newPropertyFilter,
                    ValueProvider = bindingContext.ValueProvider
                };
    
                return newBindingContext;
            }
    
            internal object UpdateCollection(ControllerContext controllerContext, ModelBindingContext bindingContext, Type elementType)
            {
                bool stopOnIndexNotFound;
                IEnumerable<string> indexes;
                GetIndexes(bindingContext, out stopOnIndexNotFound, out indexes);
                IModelBinder elementBinder = Binders.GetBinder(elementType);
    
                // build up a list of items from the request
                List<object> modelList = new List<object>();
                foreach (string currentIndex in indexes)
                {
                    string subIndexKey = CreateSubIndexName(bindingContext.ModelName, currentIndex);
                    if (!bindingContext.ValueProvider.ContainsPrefix(subIndexKey))
                    {
                        if (stopOnIndexNotFound)
                        {
                            // we ran out of elements to pull
                            break;
                        }
                        else
                        {
                            continue;
                        }
                    }
    
                    ModelBindingContext innerContext = new ModelBindingContext()
                    {
                        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, elementType),
                        ModelName = subIndexKey,
                        ModelState = bindingContext.ModelState,
                        PropertyFilter = bindingContext.PropertyFilter,
                        ValueProvider = bindingContext.ValueProvider
                    };
                    object thisElement = elementBinder.BindModel(controllerContext, innerContext);
    
                    // we need to merge model errors up
                    AddValueRequiredMessageToModelState(controllerContext, bindingContext.ModelState, subIndexKey, elementType, thisElement);
                    modelList.Add(thisElement);
                }
    
                // if there weren't any elements at all in the request, just return
                if (modelList.Count == 0)
                {
                    return null;
                }
    
                // replace the original collection
                object collection = bindingContext.Model;
                CollectionHelpers.ReplaceCollection(elementType, collection, modelList);
                return collection;
            }
          
            internal object UpdateDictionary(ControllerContext controllerContext, ModelBindingContext bindingContext, Type keyType, Type valueType)
            {
                bool stopOnIndexNotFound;
                IEnumerable<string> indexes;
                GetIndexes(bindingContext, out stopOnIndexNotFound, out indexes);
    
                IModelBinder keyBinder = Binders.GetBinder(keyType);
                IModelBinder valueBinder = Binders.GetBinder(valueType);
    
                // build up a list of items from the request
                List<KeyValuePair<object, object>> modelList = new List<KeyValuePair<object, object>>();
                foreach (string currentIndex in indexes)
                {
                    string subIndexKey = CreateSubIndexName(bindingContext.ModelName, currentIndex);
                    string keyFieldKey = CreateSubPropertyName(subIndexKey, "key");
                    string valueFieldKey = CreateSubPropertyName(subIndexKey, "value");
    
                    if (!(bindingContext.ValueProvider.ContainsPrefix(keyFieldKey) && bindingContext.ValueProvider.ContainsPrefix(valueFieldKey)))
                    {
                        if (stopOnIndexNotFound)
                        {
                            // we ran out of elements to pull
                            break;
                        }
                        else
                        {
                            continue;
                        }
                    }
    
                    // bind the key
                    ModelBindingContext keyBindingContext = new ModelBindingContext()
                    {
                        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, keyType),
                        ModelName = keyFieldKey,
                        ModelState = bindingContext.ModelState,
                        ValueProvider = bindingContext.ValueProvider
                    };
                    object thisKey = keyBinder.BindModel(controllerContext, keyBindingContext);
    
                    // we need to merge model errors up
                    AddValueRequiredMessageToModelState(controllerContext, bindingContext.ModelState, keyFieldKey, keyType, thisKey);
                    if (!keyType.IsInstanceOfType(thisKey))
                    {
                        // we can't add an invalid key, so just move on
                        continue;
                    }
    
                    // bind the value
                    ModelBindingContext valueBindingContext = new ModelBindingContext()
                    {
                        ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(null, valueType),
                        ModelName = valueFieldKey,
                        ModelState = bindingContext.ModelState,
                        PropertyFilter = bindingContext.PropertyFilter,
                        ValueProvider = bindingContext.ValueProvider
                    };
                    object thisValue = valueBinder.BindModel(controllerContext, valueBindingContext);
    
                    // we need to merge model errors up
                    AddValueRequiredMessageToModelState(controllerContext, bindingContext.ModelState, valueFieldKey, valueType, thisValue);
                    KeyValuePair<object, object> kvp = new KeyValuePair<object, object>(thisKey, thisValue);
                    modelList.Add(kvp);
                }
    
                // if there weren't any elements at all in the request, just return
                if (modelList.Count == 0)
                {
                    return null;
                }
    
                // replace the original collection
                object dictionary = bindingContext.Model;
                CollectionHelpers.ReplaceDictionary(keyType, valueType, dictionary, modelList);
                return dictionary;
            }
          
            private static bool ShouldPerformRequestValidation(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
                if (controllerContext == null || controllerContext.Controller == null || bindingContext == null || bindingContext.ModelMetadata == null)
                {
                    // To make unit testing easier, if the caller hasn't specified enough contextual information we just default
                    // to always pulling the data from a collection that goes through request validation.
                    return true;
                }
    
                // We should perform request validation only if both the controller and the model ask for it. This is the
                // default behavior for both. If either the controller (via [ValidateInput(false)]) or the model (via [AllowHtml])
                // opts out, we don't validate.
                return (controllerContext.Controller.ValidateRequest && bindingContext.ModelMetadata.RequestValidationEnabled);
            }
    
            private void BindProperties(ControllerContext controllerContext, ModelBindingContext bindingContext)
            {
                IEnumerable<PropertyDescriptor> properties = GetFilteredModelProperties(controllerContext, bindingContext);
                foreach (PropertyDescriptor property in properties)
                {
                    BindProperty(controllerContext, bindingContext, property);
                }
            }
    
            private static void GetIndexes(ModelBindingContext bindingContext, out bool stopOnIndexNotFound, out IEnumerable<string> indexes)
            {
                string indexKey = CreateSubPropertyName(bindingContext.ModelName, "index");
                ValueProviderResult vpResult = bindingContext.ValueProvider.GetValue(indexKey);
    
                if (vpResult != null)
                {
                    string[] indexesArray = vpResult.ConvertTo(typeof(string[])) as string[];
                    if (indexesArray != null)
                    {
                        stopOnIndexNotFound = false;
                        indexes = indexesArray;
                        return;
                    }
                }
    
                // just use a simple zero-based system
                stopOnIndexNotFound = true;
                indexes = GetZeroBasedIndexes();
            }
          
            private static IEnumerable<string> GetZeroBasedIndexes()
            {
                for (int i = 0; ; i++)
                {
                    yield return i.ToString(CultureInfo.InvariantCulture);
                }
            }
           
         
            private static void AddValueRequiredMessageToModelState(ControllerContext controllerContext, ModelStateDictionary modelState, string modelStateKey, Type elementType, object value)
            {
                if (value == null && !TypeHelpers.TypeAllowsNullValue(elementType) && modelState.IsValidField(modelStateKey))
                {
                    modelState.AddModelError(modelStateKey, GetValueRequiredResource(controllerContext));
                }
            }
           
            private static string GetValueRequiredResource(ControllerContext controllerContext)
            {
                return "PropertyValueRequired";
            }
    
            private static object ConvertProviderResult(ModelStateDictionary modelState, string modelStateKey, ValueProviderResult valueProviderResult, Type destinationType)
            {
                try
                {
                    object convertedValue = valueProviderResult.ConvertTo(destinationType);
                    return convertedValue;
                }
                catch (Exception ex)
                {
                    modelState.AddModelError(modelStateKey, ex);
                    return null;
                }
            }
    
            private static class CollectionHelpers
            {
    
                private static readonly MethodInfo _replaceCollectionMethod = typeof(CollectionHelpers).GetMethod("ReplaceCollectionImpl", BindingFlags.Static | BindingFlags.NonPublic);
                private static readonly MethodInfo _replaceDictionaryMethod = typeof(CollectionHelpers).GetMethod("ReplaceDictionaryImpl", BindingFlags.Static | BindingFlags.NonPublic);
    
                [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
                public static void ReplaceCollection(Type collectionType, object collection, object newContents)
                {
                    MethodInfo targetMethod = _replaceCollectionMethod.MakeGenericMethod(collectionType);
                    targetMethod.Invoke(null, new object[] { collection, newContents });
                }
    
                private static void ReplaceCollectionImpl<T>(ICollection<T> collection, IEnumerable newContents)
                {
                    collection.Clear();
                    if (newContents != null)
                    {
                        foreach (object item in newContents)
                        {
                            // if the item was not a T, some conversion failed. the error message will be propagated,
                            // but in the meanwhile we need to make a placeholder element in the array.
                            T castItem = (item is T) ? (T)item : default(T);
                            collection.Add(castItem);
                        }
                    }
                }
    
                [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
                public static void ReplaceDictionary(Type keyType, Type valueType, object dictionary, object newContents)
                {
                    MethodInfo targetMethod = _replaceDictionaryMethod.MakeGenericMethod(keyType, valueType);
                    targetMethod.Invoke(null, new object[] { dictionary, newContents });
                }
    
                private static void ReplaceDictionaryImpl<TKey, TValue>(IDictionary<TKey, TValue> dictionary, IEnumerable<KeyValuePair<object, object>> newContents)
                {
                    dictionary.Clear();
                    foreach (KeyValuePair<object, object> item in newContents)
                    {
                        // if the item was not a T, some conversion failed. the error message will be propagated,
                        // but in the meanwhile we need to make a placeholder element in the dictionary.
                        TKey castKey = (TKey)item.Key; // this cast shouldn't fail
                        TValue castValue = (item.Value is TValue) ? (TValue)item.Value : default(TValue);
                        dictionary[castKey] = castValue;
                    }
                }
            }
    
        }
    
        internal delegate bool TryGetValueDelegate(object dictionary, string key, out object value);
        internal static class TypeHelpers
        {
    
            private static readonly Dictionary<Type, TryGetValueDelegate> _tryGetValueDelegateCache = new Dictionary<Type, TryGetValueDelegate>();
            private static readonly ReaderWriterLockSlim _tryGetValueDelegateCacheLock = new ReaderWriterLockSlim();
    
            private static readonly MethodInfo _strongTryGetValueImplInfo = typeof(TypeHelpers).GetMethod("StrongTryGetValueImpl", BindingFlags.NonPublic | BindingFlags.Static);
    
            public static readonly Assembly MsCorLibAssembly = typeof(string).Assembly;
            public static readonly Assembly MvcAssembly = typeof(Controller).Assembly;
            public static readonly Assembly SystemWebAssembly = typeof(HttpContext).Assembly;
    
            // method is used primarily for lighting up new .NET Framework features even if MVC targets the previous version
            // thisParameter is the 'this' parameter if target method is instance method, should be null for static method
            public static TDelegate CreateDelegate<TDelegate>(Assembly assembly, string typeName, string methodName, object thisParameter) where TDelegate : class
            {
                // ensure target type exists
                Type targetType = assembly.GetType(typeName, false /* throwOnError */);
                if (targetType == null)
                {
                    return null;
                }
    
                return CreateDelegate<TDelegate>(targetType, methodName, thisParameter);
            }
    
            public static TDelegate CreateDelegate<TDelegate>(Type targetType, string methodName, object thisParameter) where TDelegate : class
            {
                // ensure target method exists
                ParameterInfo[] delegateParameters = typeof(TDelegate).GetMethod("Invoke").GetParameters();
                Type[] argumentTypes = Array.ConvertAll(delegateParameters, pInfo => pInfo.ParameterType);
                MethodInfo targetMethod = targetType.GetMethod(methodName, argumentTypes);
                if (targetMethod == null)
                {
                    return null;
                }
    
                TDelegate d = Delegate.CreateDelegate(typeof(TDelegate), thisParameter, targetMethod, false /* throwOnBindFailure */) as TDelegate;
                return d;
            }
    
            public static TryGetValueDelegate CreateTryGetValueDelegate(Type targetType)
            {
                TryGetValueDelegate result;
    
                _tryGetValueDelegateCacheLock.EnterReadLock();
                try
                {
                    if (_tryGetValueDelegateCache.TryGetValue(targetType, out result))
                    {
                        return result;
                    }
                }
                finally
                {
                    _tryGetValueDelegateCacheLock.ExitReadLock();
                }
    
                Type dictionaryType = ExtractGenericInterface(targetType, typeof(IDictionary<,>));
    
                // just wrap a call to the underlying IDictionary<TKey, TValue>.TryGetValue() where string can be cast to TKey
                if (dictionaryType != null)
                {
                    Type[] typeArguments = dictionaryType.GetGenericArguments();
                    Type keyType = typeArguments[0];
                    Type returnType = typeArguments[1];
    
                    if (keyType.IsAssignableFrom(typeof(string)))
                    {
                        MethodInfo strongImplInfo = _strongTryGetValueImplInfo.MakeGenericMethod(keyType, returnType);
                        result = (TryGetValueDelegate)Delegate.CreateDelegate(typeof(TryGetValueDelegate), strongImplInfo);
                    }
                }
    
                // wrap a call to the underlying IDictionary.Item()
                if (result == null && typeof(IDictionary).IsAssignableFrom(targetType))
                {
                    result = TryGetValueFromNonGenericDictionary;
                }
    
                _tryGetValueDelegateCacheLock.EnterWriteLock();
                try
                {
                    _tryGetValueDelegateCache[targetType] = result;
                }
                finally
                {
                    _tryGetValueDelegateCacheLock.ExitWriteLock();
                }
    
                return result;
            }
    
            public static Type ExtractGenericInterface(Type queryType, Type interfaceType)
            {
                Func<Type, bool> matchesInterface = t => t.IsGenericType && t.GetGenericTypeDefinition() == interfaceType;
                return (matchesInterface(queryType)) ? queryType : queryType.GetInterfaces().FirstOrDefault(matchesInterface);
            }
    
            public static object GetDefaultValue(Type type)
            {
                return (TypeAllowsNullValue(type)) ? null : Activator.CreateInstance(type);
            }
    
            public static bool IsCompatibleObject<T>(object value)
            {
                return (value is T || (value == null && TypeAllowsNullValue(typeof(T))));
            }
    
            public static bool IsNullableValueType(Type type)
            {
                return Nullable.GetUnderlyingType(type) != null;
            }
    
            private static bool StrongTryGetValueImpl<TKey, TValue>(object dictionary, string key, out object value)
            {
                IDictionary<TKey, TValue> strongDict = (IDictionary<TKey, TValue>)dictionary;
    
                TValue strongValue;
                bool retVal = strongDict.TryGetValue((TKey)(object)key, out strongValue);
                value = strongValue;
                return retVal;
            }
    
            private static bool TryGetValueFromNonGenericDictionary(object dictionary, string key, out object value)
            {
                IDictionary weakDict = (IDictionary)dictionary;
    
                bool containsKey = weakDict.Contains(key);
                value = (containsKey) ? weakDict[key] : null;
                return containsKey;
            }
    
            public static bool TypeAllowsNullValue(Type type)
            {
                return (!type.IsValueType || IsNullableValueType(type));
            }
        }

     

     

  • 相关阅读:
    「CSP-S2020」贪吃蛇
    CSP-S 2020 游记
    「POI2012」Leveling Ground
    「THUPC2019」鸭棋
    「SNOI2020」取石子 题解
    第一篇随笔
    实验三 面向对象分析与设计
    高斯模型
    实验二 结构化分析与设计
    极大似然估计
  • 原文地址:https://www.cnblogs.com/majiang/p/2663421.html
Copyright © 2020-2023  润新知