今天看见一同事写了一段代码很是奇怪,大致结构如下:
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));
}
}