Skip to content

Instantly share code, notes, and snippets.

@UltraSabreman
Last active August 29, 2015 14:23
Show Gist options
  • Save UltraSabreman/c561bbd948f8339ec534 to your computer and use it in GitHub Desktop.
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.
/// <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