I'm creating a generic object from DB data:
object[] data = new object[dataReader.FieldCount];
dataReader.GetValues(data);
T t = (T)Activator.CreateInstance(typeof(T), data);
But, types with no constructor error on the third line. I want to add an if:
if (typeof(T).GetConstructor(data.TypesOf()) != null)
data.TypesOf() is actually an array - Type[] - that contains all types of the objects in data.
What is the equivalent to data.TypesOf() that really works?
Or do I need to iterate data and build it myself?
I'm assuming your object[] is containing values that are, for example, an int, a string and a float, and you are trying to resolve a constructor of the form public T(int,string,float). To get the types, you could use:
var types = Array.ConvertAll(data, x => x.GetType());
But that won't actually help you much here, since that is pretty-much what Activator.CreateInstance(typeof(T), data) has already tried to do - so if Activator failed, I don't see that it is obvious that you're going to do any better - unless the key difference is that the constructor is non-public and you are going to supply some BindingFlags.
Personally, though, I would suggest that it is easier to bind by name than by position; there are tools like dapper that will do all of that for you, allowing you to use simply:
var data = conn.Query<SomeType>(sql, args).ToList();
for example:
int userId = 12345;
var users = conn.Query<User>("select * from Users where Id = #userId",
new {userId}).SingleOrDefault();
As far as I understood you are trying to get types of elememnts of an object array. So you can do something like:
var ctorArgsTypes = data.Select(d => d.GetType()).ToArray()
var ctor = typeof(T).GetConstructor(ctorArgsType);
// check if appropriate ctor exists
if (ctor == null)
throw something
T t = (T)Activator.CreateInstance(typeof(T), data);
When you look at the Activator.CreateInstance(Type, Object[]) method, for the Object[] paramter :
An array of arguments that match in number, order, and type the
parameters of the constructor to invoke. If args is an empty array or
null, the constructor that takes no parameters (the default
constructor) is invoked.
So maybe in your case, your data object is not typed for each value (if getting from a DB or a file). You need to find a way to "type" your objects in your data array.
Just use Type.GetTypeArray(object[]).
https://msdn.microsoft.com/en-us/library/system.type.gettypearray(v=vs.110).aspx
Related
I am trying to invoke a generic method. The definition of the method is as follows:
public System.Collections.Generic.IList<T> Query<T>(string query, [string altUrl = ""])
where T : new()
This is from the SalesforceSharp library on github. I am trying to make an additional service layer over this call and am struggling to invoke it. See my code below.
public List<T> Query<T>()
{
//IList<Salesforce.Account> _returnList = null;
IList<T> _returnList = null;
Type _t = typeof(T);
SqlBuilder _sb = new SqlBuilder();
_sb.Table = _t.Name.ToString();
foreach (PropertyInfo p in _t.GetProperties()) _sb.Fields.Add(p.Name.ToString());
MethodInfo method = _Client.GetType().GetMethod("Query");
method = method.MakeGenericMethod(_t);
try
{
object[] _prms = new object[1];
_prms[0] = _sb.SQL;
_returnList = (IList<T>)method.Invoke(_Client, new object[] { _prms });
//_returnList = _Client.Query<Salesforce.Account>(_sb.SQL);
}
catch { }
return (List<T>)_returnList;
}
If I run this i get a Parameter Count Mismatch exception on the method.invoke line, but i am confused because if i bring in the two uncommented lines and execute without the generic call it is working ok. I have tried many combinations of string arrays wrapped in object arrays, strings in strings, etc but can't get it to go. I thought maybe it was treating the second parameter as mandatory? but adding another object to my _prms array didnt work either.
Please help!
Thanks,
Dom
For optional parameters that you don't want to specify a value for, you have to pass Type.Missing like this:
_returnList = (IList<T>)method.Invoke(_Client, new object[] { _sb.SQL, Type.Missing });
Quoting from this reference:
Use the Missing field for invocation through reflection to obtain the default value of a parameter
Please note also that another problem is that you are currently passing an object[] instead of just the query string.
The optional parameters are just syntactic sugars, when you don't supply an optional parameter compiler calls the method with the given default value. But in case of Reflection, you need to do it manually.
object[] _prms = new object[2];
_prms[0] = _sb.SQL;
_prms[1] = "";
_returnList = (IList<T>)method.Invoke(_Client, _prms);
I would like to make my code convention-based by using Types and keeping things simple, but generics has it's own complexity with it's own learning curve.
I have a bunch of POCOs (Plain Old CLR Objects) in a List that I'd like to iterate through later in the code.
var models = new List<Type>();
models.Add(typeof(Person));
models.Add(typeof(Company));
Would like to cycle through each list item:
models.ForEach(m =>
{
var label = m.FullName;
// var data = JsonConvert.DeserializeObject<List<typeof(m)>>(""); // doesn't work
var data = JsonConvert.DeserializeObject<List<m>>(""); // doesn't work either
...
}
The issue is that the "m" in the Deserialize line isn't working. What would be the best way to pass that through, i.e. making the 'List<m>' a 'List<T>' that we can use?
To use generics, you really need to know the Type (T) at compile time, you don't - you know it at run time. (Caveat: Its possible with reflection, but theres no need to use it when there's an overload as described below)
There is an overload of DeserializeObject which takes a Type rather than use generics. So your code would be
models.ForEach(m =>
{
var label = m.FullName;
var data = JsonConvert.DeserializeObject("",m);
...
}
However, as you've pointed out in comments you actually need a List<T> not a single T. You'll need a little bit of reflection, just to create the right type to pass to the above DeserializeObject call.
var tList = typeof(List<>); // Type of open List
models.ForEach(m =>
{
var label = m.FullName;
var tConvert = = tList.MakeGenericType(m);
var data = JsonConvert.DeserializeObject("",tConvert);
...
}
The answer to your question is above, but the more I look at it the harder it is to see what you can actually do with data. all you'll ever know about data is that it is an object. You cant cast it to anything - you wont know if its a list of Person or a list of Company.
Perhaps this was an overly contrived example you've used for a real-life problem. If not I forsee you're next problem is what to do with data!!
If you don't know the type at compile time you can do this with Reflection. Consider the following code:
models.ForEach(m =>
{
var mi = JsonConvert.GetType()
.GetMethod("DeserializeObject");
var m = mi.MakeGenericMethod(new[] { m });
// you pass [null] for the object because it's a [static] method
// and you don't have to declare [args] if you can express it simply
// but keep in mind that it's simply an object[]
m.Invoke(null, args);
}
Another solution is to call the generic method using reflection (if there isn't any overload that takes the type as parameter)
models.ForEach(m =>
{
MethodInfo method = typeof(JsonConvert).GetMethod("DeserializeObject");
MethodInfo generic = method.MakeGenericMethod(m);
generic.Invoke(null, "");
}
[TestMethod]
public void TestMyGenericBaseClasses()
{
Type typeCrazy = ThisPartyIsTypeCrazyWOOT();
// How do I create a generic object?
MadnessOhYeah<typeCrazy> sanity = new MadnessOhYeah<typeCrazy>();
// How do I use a generic object after it has been created?
Assert.IsTrue(sanity.MyTrueFunction(), "this is sparta");
// How do I call a generic function generically?
bool result = MyFunction<typeCrazy>();
Assert.IsTrue(result, "I did not get my teeth whitened!");
}
Is there any way to make this compile? (ThisPartyIsTypeCrazyWOOT returns a Type) Because this is a test, we're not concerned about having to use reflection or anything, unless that's just absolutely crazy.
I'm getting the vibe that this isn't going to be possible though, and that our test functions will just have to be more specific.
You need Type.MakeGenericType Method. Then Activator.CreateInstance Method.
Update 2: The first example I posted still doesn't 100% answer the question, since it involves a cast to List<int>, which is a type that is known at compile time. Below is a reflection-only solution that illustrates how you can use a generic type knowing nothing about the type argument itself. But, as you can see, it's... well, disgusting ;)
Type userType = GetUserSuppliedType();
// Now let's say userType is T.
// Then here we are getting the type typeof(List<T>).
// But, of course, there's no way to have any such information in the code.
Type listOfUserType = typeof(List<>).MakeGenericType(new[] { userType });
// This is effectively calling new List<T>();
object listObject = Activator.CreateInstance(listOfUserType);
// Do you see how messy this is getting?
MethodInfo addMethod = listOfUserType.GetMethod("Add");
// We better hope this matches userType!
object input = GetUserSuppliedInput();
// I suppose we could check it, to be sure...
if (input == null || input.GetType() != userType)
{
throw new InvalidOperationException("That isn't going to work!");
}
// Here we are finally calling List<T>.Add(input) -- just in the most ass-
// backwards way imaginable.
addMethod.Invoke(listObject, new[] { input });
Update: OK, if you insist on doing this, here's an example of how it's possible—but very cumbersome!
Type genericListType = typeof(List<>);
Type listOfInt32Type = genericListType.MakeGenericType(new[] { typeof(int) });
object listObject = Activator.CreateInstance(listOfInt32Type);
List<int> list = (List<int>)listObject;
list.Add(1);
Generics can't quite work like this because a Type object could be anything. Consider this code:
var list = new List<int>();
list.Add(1);
The type of list in the above code is known to be List<int>, which defines what operations are legal on list such as Add(1).
Now consider this instead:
Type t = GetTypeFromIndeterminateSourceSuchAsUserInput();
var list = new List<t>();
list.Add(?);
When t is a Type object rather than the name of a type (like int) which the compiler can parse, it's not really possible to instantiate a generic type using that type—or rather, it's possible (see Andrey's answer), but you can't really use the resulting object in any sort of generic way.
Now, you might think that something like this ought to work:
Type t = typeof(int);
var list = new List<t>();
list.Add(1);
...but just because the value of t is known (by you) at compile time doesn't change the way things work in general.
Anyway, yes it's possible using reflection; but if you go down that path, you're committing to a pretty reflection-heavy solution. What I'm getting at is that in general it isn't a particularly realistic thing to do.
Consider the following:
// select a subset of the DataTable
var subset = DataTable.Where(...).Select(row => new
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
// or create a new object
var subset = new {
Id = 1,
Name = "something random",
Email = "name#domain.tld"
};
Is there any way to use the subset variable as a parameter to a method, without it being cast as a plain Object? Can you somehow carry the auto-generated type of the variable?
I am trying to avoid having to create new classes every time I want to pass LINQ subsets to methods.
Random generic approaches are welcome.
No, passing anonymous types about isn't generally a good idea because you lose the type information*. You should create a concrete type and use that instead.
var subset = DataTable.Where(...).Select(row => new SomeType
{
Id = Convert.ToInt32(row["Id"]),
Name = row["Name"].ToString(),
Email = row["Email"].ToString()
});
Alternatively you can use the Tuple type if you are using .NET 4. This is a simple way to create "disposable" types and still get some type-safety.
*Actually there is a workaround, but I regard it is an ugly hack and would advise that you don't do it.
If I need to do this, I use resharper's "Replace Anonymous Type With Named Class" refactoring option. Then you have an appropriate named type to expose over the API, and you haven't had to do any work. This also gives you options to create it immutable (like anonymous types) or mutable, nested vs top-level, etc.
BTW, I don't recommend struct here (from the question).
Another option is to pass the behaviour into the method - i.e. an Action<int,string,string> callback - then do something like:
foreach(item in query) callback(item);
However, I don't like this as it is not obvious that there is a likely error in:
DoSomething(args, (id, email, name) => Email(To: email, Subject: name));
(the error being that it should probably be (id, name, email), if you see what I mean)
You can use a generic method:
public static void Foo<T>(T item)
{
// Do whatever
}
Then if you call
Foo(subset);
the compiler will infer T for you. Whether or not that actually helps you is another matter... it depends on what the method is meant to do. Obviously Foo can't refer to Id, Name, Email etc.
In general, if multiple methods should know about the same members, then you should use a named type. The usual case for passing them to generic methods is where the method really doesn't care about what type is involved, such as in LINQ.
I've made a feature request for C# 5 that we should be able to create types which have all the same features as anonymous types (immutability, equality, hash code generation, ToString dumping) but for simple named types. We'll see if it actually happens...
Anonymous Types don't provide much help outside of the context they where created.
If you need to pass an Anonymous Type to a method, either this method is very generic like (Example)
void PrintAllObjectProperties(object obj);
witch you would use reflection to do the work, or you are doing something wrong.
Here's what I came up with...
Extension method on Object:
public static class ObjectExtensions
{
/// <summary>
/// Cast Object to anonymous type.
/// E.G.: new Object().ToAnonymousType(new { Property = new Type() });
/// </summary>
public static T ToAnonymousType<T>(this Object o, T t)
{
return (T)o;
}
}
Usage:
public void HandleAnonymousTypeAsParameter(Object o)
{
var anonymousType = o.ToAnonymousType(new
{
Id = new Int32(),
Foo = new String(),
Bar = new String()
});
// ... You can do this in even less characters:
var anonymousType = o.ToAnonymousType(new { Id = 0, Foo = "", Bar = "" });
}
HandleAnonymousTypeAsParameter(new
{
Id = 1,
Foo = "foo",
Bar = "bar"
});
Credits goes to John Skeet and Thomas P.
This question already has answers here:
How do I use reflection to call a generic method?
(8 answers)
Closed 9 years ago.
Is it possible to declare an instance of a generic without knowing the type at design-time?
Example:
Int i = 1;
List<typeof(i)> list = new List<typeof(i)>();
where the type of i could be anything, instead of having to do:
List<int> list = new List<int();
If you don't know the type at compile-time, but you want the actual type (i.e. not List<object>) and you're not in a generic method/type with the appropriate type parameter, then you have to use reflection.
To make the reflection simpler, I've sometimes introduced a new generic type or method in my own code, so I can call that by reflection but then just use normal generics after that. For example:
object x = GetObjectFromSomewhere();
// I want to create a List<?> containing the existing
// object, but strongly typed to the "right" type depending
// on the type of the value of x
MethodInfo method = GetType().GetMethod("BuildListHelper");
method = method.MakeGenericMethod(new Type[] { x.GetType() });
object list = method.Invoke(this, new object[] { x });
// Later
public IList<T> BuildListHelper<T>(T item)
{
List<T> list = new List<T>();
list.Add(item);
return list;
}
Of course, you can't do an awful lot with the list afterwards if you don't know the type... that's why this kind of thing often falls down. Not always though - I've used something like the above on a few occasions, where the type system just doesn't quite let me express everything I need to statically.
EDIT: Note that although I'm calling Type.GetMethod in the code above, if you were going to execute it a lot you'd probably want to just call it once - after all, the method isn't going to change. You may be able to make it static (you could in the case above) and you probably want to make it private too. I left it as a public instance method for the simplicity of the GetMethod call in sample code - you'd need to specify the appropriate binding flags otherwise.
If you don't know the type at design-time, I'd say you have a list of OBJECTS (the base class for all other types).
List<object> list = new List<object>();
I think the best you are going to be able to do is something like this:
static void Main(string[] args)
{
int i = 1;
var thelist = CreateList(i);
}
public static List<T> CreateList<T>(T t)
{
return new List<T>();
}
You can also use Activator.CreateInstance. Example code snippet:
public class BaseRepository<T> where T : DataContext
{
protected T _dc;
public BaseRepository(string connectionString)
{
_dc = (T) Activator.CreateInstance(typeof(T), connectionString);
}
public void SubmitChanges()
{
_dc.SubmitChanges();
}
}
If you still want to type .Add(), .Remove(), do foreach etc. you can treat the List as a regular "old" System.Collections.IList,
since this interface is luckily implemented by List<T>.
And since all other posted answers to this question shows pretty much every other possible way to create an instance of a List<T> dynamically,
i will show one last way to do it.
I personally use this method when creating generic instances, when i don't really know nothing about the type at compile time,
and the type must be passed as a string, perhaps coming from the application configuration file.
In this example, T is System.String for simplicity but it could be anything:
Type T = typeof ( string ); // replace with actual T
string typeName = string.Format (
"System.Collections.Generic.List`1[[{0}]], mscorlib", T.AssemblyQualifiedName );
IList list = Activator.CreateInstance ( Type.GetType ( typeName ) )
as IList;
System.Diagnostics.Debug.Assert ( list != null ); //
list.Add ( "string 1" ); // new T
list.Add ( "string 2" ); // new T
foreach ( object item in list )
{
Console.WriteLine ( "item: {0}", item );
}