Last active
August 29, 2015 14:23
-
-
Save UltraSabreman/c561bbd948f8339ec534 to your computer and use it in GitHub Desktop.
Instantiate almost any obect by it's type, as long as it has a constructor OR a factory method. Some may fail due to needing specific string formats. This also allows you to add a single element to any collection that impliments an "Add" method.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// This will Instantiate almost any object with garbage/default values | |
/// as long as that object has either 1) a constructor or 2) a static factory method. | |
/// | |
/// Some objects may fail due to needing a strictly formmated string as an argument. | |
/// </summary> | |
/// <param name="objType">The type of the object you wish to create</param> | |
/// <returns>The created object or NULL if creation failed.</returns> | |
/// | |
public object InstantiateObject(Type objType, bool AddToCollection = false) { | |
return InstantiateObject(objType, 0, AddToCollection); | |
} | |
private object InstantiateObject(Type objType, int depth, bool AddToCollection = false) { | |
if (++depth > 5) return null; | |
Object ret = null; | |
//First we take care of all value types. | |
if (objType.IsValueType) | |
ret = Activator.CreateInstance(objType); | |
else { | |
//Then we check to see if the object has any constructors. | |
var constructors = objType.GetConstructors(); | |
if (constructors.Count() > 0) { | |
//If it does, we find one with the lease arguments, | |
ret = ExecuteEasiestMethod(constructors, depth); | |
} else { | |
//If we can't find a constructor, we look for a static factory method | |
List<MethodInfo> methods = new List<MethodInfo>(objType.GetMethods()); | |
methods.RemoveAll(x => !x.IsStatic || x.ReturnType != objType); | |
ret = ExecuteEasiestMethod(methods.ToArray(), depth); | |
} | |
try { | |
// So the problem here is as follows: if something inherits from Ienumarable, it probably | |
// has a collection of items to enumarate over. Unfortunatly, you can't directly add | |
// to a raw IEnumarable child, so we assume that it has an "Add" method. If it doesn't | |
// exist, this operation will fail silently. | |
if (AddToCollection && isEnumarable(objType)) { | |
Type gen = objType; | |
while (!gen.IsGenericType) | |
gen = gen.BaseType; | |
Type genArg = gen.GetGenericArguments()[0]; | |
object [] olist = new object[] { InstantiateObject(genArg, depth, true) }; | |
if (ret == null) | |
ret = (IList)typeof(List<>).MakeGenericType(genArg).GetConstructor(Type.EmptyTypes).Invoke(null); ; | |
ret.GetType().GetMethod("Add").Invoke(ret, olist); | |
} | |
} catch (Exception) { } | |
} | |
return ret; | |
} | |
/// <summary> | |
/// Simply checks if a type is enumarable | |
/// </summary> | |
/// <param name="type">The type to check</param> | |
/// <returns>True if it's enumarable, false otherwise</returns> | |
private bool isEnumarable(Type type) { | |
return type.GetInterface(typeof(IEnumerable).FullName) != null; ; | |
} | |
/// <summary> | |
/// This takes a list of methods, sorts them by paramater count, | |
/// and executes them in order from most to least arguments. | |
/// This ensures we populate the object as best as we can. | |
/// </summary> | |
/// <param name="methods">List of methods to sort</param> | |
/// <returns>The object the method returned</returns> | |
private object ExecuteEasiestMethod(MethodBase[] methods, int depth) { | |
if (methods == null || methods.Count() <= 0) return null; | |
List<MethodBase> sortedMethods = new List<MethodBase>(); | |
sortedMethods.AddRange(methods); | |
sortedMethods.Sort((Prev, Next) => Prev.GetParameters().Count().CompareTo(Next.GetParameters().Count())); | |
sortedMethods.Reverse(); | |
foreach (MethodBase m in sortedMethods) { | |
try { | |
ParameterInfo[] args = m.GetParameters(); | |
//If this method is a constructor, it only takes a list of args. | |
//Otherwise it would need a null arg for the object (since it's static) | |
ConstructorInfo c = m as ConstructorInfo; | |
Object[] instanceArgsOrig = null; | |
Object[] instanceArgs = null; | |
for (int i = 0; i < args.Count(); i++) { | |
try { | |
if (instanceArgs == null) { | |
instanceArgsOrig = makeParamList(args, depth); | |
instanceArgs = instanceArgsOrig; | |
} | |
if (c != null) | |
return c.Invoke(instanceArgs); | |
else | |
return m.Invoke(null, instanceArgs); | |
} catch (Exception ex) { | |
//Here we try to compensate for arguments being invalid. | |
//If it's an argument exception, we (try to) replace the returned arg with null | |
//If it's a format exception, we try to replace one string at a time with null, and then | |
//all of them. | |
//This is all done in hopes that the method we're calling handles NULLs better then mal-formed argumets. | |
if (ex.InnerException == null) continue; | |
if (ex.InnerException is FormatException) { | |
fixArgs(ref instanceArgs, instanceArgsOrig); | |
} else if (ex.InnerException is ArgumentException) { | |
ArgumentException e = (ArgumentException)ex.InnerException; | |
for (int l = 0; l < args.Count(); l++) { | |
if (args[l].Name == e.ParamName) | |
if (instanceArgs[l] == null) | |
return null; | |
else | |
instanceArgs[l] = null; | |
} | |
} | |
} | |
} | |
} catch (Exception) { continue; } | |
} | |
return null; | |
} | |
private bool fixArgs_oneArgAtATime = true; | |
private int fixArgs_currentRepArg = 0; | |
/// <summary> | |
/// This function attempts to correct single string arguments by replaceing them win nulls | |
/// and then replacing all strings with nulls. This is the best way to | |
/// bypass mal-formed string arguments | |
/// </summary> | |
/// <param name="curArgs">A list of args that will be passed to the function</param> | |
/// <param name="origArgs">The original list of args (should be thesame as curArgs on first call)</param> | |
private void fixArgs(ref Object[] curArgs, Object[] origArgs) { | |
if (fixArgs_oneArgAtATime) { | |
//replace One arg at a time; | |
curArgs = origArgs; | |
for (; fixArgs_currentRepArg < curArgs.Count(); fixArgs_currentRepArg++) { | |
Object arg = curArgs[fixArgs_currentRepArg]; | |
if (arg is String && ((String)arg) != null) { | |
curArgs[fixArgs_currentRepArg] = null; | |
break; | |
} | |
} | |
if (fixArgs_currentRepArg >= curArgs.Count()) { | |
fixArgs_oneArgAtATime = false; | |
curArgs = origArgs; | |
fixArgs_currentRepArg = 0; | |
} | |
} else { | |
//replace all args | |
for (int l = 0; l < curArgs.Count(); l++) { | |
Object arg = curArgs[l]; | |
if (arg is String && ((String)arg) != null) { | |
curArgs[l] = null; | |
break; | |
} | |
} | |
} | |
} | |
/// <summary> | |
/// This is a helper method. It instantiates all passed in parameters | |
/// to a default value. | |
/// </summary> | |
/// <param name="parameterList">Paramaters to instantiate</param> | |
/// <returns>The final objects, or null if none.</returns> | |
private Object [] makeParamList(ParameterInfo [] parameterList, int depth) { | |
if (parameterList == null || parameterList.Count() <= 0) return null; | |
List<Object> paramList = new List<object>(); | |
foreach (var param in parameterList) { | |
//So the problem here is that if it's a string, we really dont want it to be NULL | |
//Because if you where to serialize the object later, it would be empty. | |
//This is why I set it to "EXAMPLE" instead. | |
//This probably breaks more objects (since some might be able to handle a null) | |
//but it's a good trade off in my case. | |
//If the param has a defualt value, use it. | |
if (param.HasDefaultValue) | |
paramList.Add(param.DefaultValue); | |
else { | |
if (param.ParameterType == typeof(String)) | |
paramList.Add("EXAMPLE STRING"); | |
else | |
paramList.Add(InstantiateObject(param.ParameterType, depth, true)); | |
} | |
} | |
return paramList.ToArray(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment