I've registered a custom type handler for Dapper to Json serialize a string. This works as expected. However, whilst coding, I discovered two pieces of code after refactoring, the latter one which didn't trigger the datatype handler, but should in my opinion, so what's the difference?
First the code that works as expected - the custom type handler is called by dapper, and the data is serialized when inserted into the table:
if (val.GetType() != typeof (String))
{
var v = new JsonString {Value = val};
this.Connection.Execute("insert into misc (k,v) values (#keyName, #v)",
new { keyName, v });
}
else
{
this.Connection.Execute("insert into misc (k,v) values (#keyName, #val)",
new { keyName, val });
}
Now for the code which doesn't work as it inserts into the table the fully qualified type string instead of the serialized data, but which I think is semantically similar:
var v = val.GetType() != typeof (String)
? new JsonString {Value = val}
: val;
// t is set to type of JsonString, therefore the dapper type handler should be used
var t = v.GetType();
this.Connection.Execute("insert into misc (k,v) values (#keyName, #v)",
new { keyName, v });
Is it a dapper bug or a c# oddity? I assume it's something to do with the auto typing in the ternary conditional which is the failing point, but t is set to the data type that is serialized by Dapper (or rather the custom type handler). What's the difference?
I am going to assume that the compile-time type (i.e. declaration type) of val is System.Object. I will explain why the two situations are not equivalent.
One must be careful to distinguish between the compile-time type of a variable and the actual run-time type (which is found by .GetType()).
In the first piece of code, in the branch where val is not String at run-time, we declare:
var v = new JsonString {Value = val};
Here var is substituted by JsonString since that is the compile-time type of the right-hand side of the = assignment. Then the anonymous type instance:
new { keyName, v }
is going to be a class (I will call it class Anonymous1) with a member
public JsonString v { get { ... } }
Now, in the second piece of code, we have instead:
var v = val.GetType() != typeof (String)
? new JsonString {Value = val}
: val;
The compile-time types of the operands to the ternary operator are:
{bool} ? {JsonString} : {object}
At compile-time, a best common type for JsonString and object must be found. That common type is object. So object becomes the compile-time type of v here, i.e. var means object here.
Then the anonymous type:
new { keyName, v }
is a type "Anonumous2" whose "2nd" property reads:
public object v { get { ... } }
So to sum up: In the first case you pass in an object which has a property v declared as a JsonString which when retrieved returns a JsonSting that happens to have run-time JsonString. In the second code sample you pass in an object which has a property v declared as object which when retrieved returns an object that happens to have run-time type JsonString.
I do not know much on how Dapper works! But presumably when it sees (by reflection?) that the property type is object, it simply calls .ToString() on it. If that object happens to be of run-time type string, that should be OK, since string.ToString() is overridden. But JsonString.ToString() is not like that.
When Dapper sees the property is declared as JsonString, Dapper does something smarter than calling .ToString().
Assuming there is no implicit conversion available between JsonString and String, the code in the second example won't work. If there is an implicit conversion available, you need to provide more information about the exception that is occurring.
WIth no conversion available between JsonString, a variable declared with type var has a type that is inferred by the compiler ((https://msdn.microsoft.com/en-us/library/bb384061.aspx). In this case, variable v is either of type JsonString or String depending upon which part of the assignment occurs. If the compiler assumes it is of type JsonString, any assignment with a String will fail.
In your second code example, the first line of code is wrong since the assignment results in two different data types getting assign to variable v.
Related
Is it possible to parse a string onto a data type that's stored in a string variable?
Example:
var dataType = "Int32";
var value = "123";
dynamic value = dataType.parse(value);
You could try the following:
Convert dataType to a Type by using Type.GetType(dataType). Keep in mind that you may need to use the full assembly-qualified name, for instance, "Int32" may not be enough and you may need to use "System.Int32".
Once you have the Type, use Convert.ChangeType(value, type) (where type is the Type variable) to convert the value to the appropiate type. Convert.ChangeType returns an object so you are going to have to cast it unless you are fine with using dynamic.
Since you're (presumably) only dealing with a handful of primitive types, a switch statement would be faster than reflection:
dynamic value
switch dataType {
case "Int32":
value = Int32.Parse(text);
case "Int64":
value = Int64.Parse(text);
case "DateTime":
value = Int32.Parse(text);
...
}
But this begs more questions - if you don't know the type at compile-time, how are you going to do anything with it? If you use a dynamic variable, then all of your code that uses that variable will be dynamic and bound at run-time as well. So if you don't need to parse the data, why not just leave it as strings?
In other words, in my experience, using reflection or dynamic to allow for different data types stored as strings only gets you so far. Whenever you go to actually use the data (in formulas, for example), you end up with either a bunch of reflection or switches like this one.
Yes, if the type name (as a string) is fully qualified. In your case, you want to be looking at System.Int32 (note we've included the namespace). So, to replicate your code above (Drop this in LINQPad):
string typeName = "System.Int32";
Type concreteType = Type.GetType(typeName);
object concreteInstance = Activator.CreateInstance(concreteType);
concreteInstance.Dump();
This will output 0 as we created an integer. However, we can avoid the above if we register converters for specific types (in this case, by string):
void Main()
{
object value = Set("System.Int32", "4");
value.Dump();
}
Dictionary<string, Func<string, object>> converters = new Dictionary<string, Func<string, object>>()
{
["System.Int32"] = o => int.Parse(o)
};
object Set(string typeName, string value)
{
if(converters.TryGetValue(typeName, out Func<string, object> converter))
return converter(value);
return null;
}
For example:I have 2 variable (value ) & (property) i want to check is cast possible for value? We do not know the type of variables, How to check if cast is possible?
var value = Reader[item];
PropertyInfo property = properties.Where(x => x.Name == item).FirstOrDefault();
var type=property.PropertyType;//Or property.ReflectedType
var cs= value as type // Error: type is variable but is used like a Type
if (cs!=null){
...
}
Sample 1:
var value = 123;//is int
type = property.GetType();// is double
var x = (double)value;//Can be casted
Sample 2:
var value = "asd";//is string
type = property.GetType();// is double
var x = (double)value;//Can not be casted
You can use IsAssignable:
bool isValidCast = type.IsAssignableFrom(value.GetType())
As per the comments about int to double:
I've made a mistake in my comment, so i deleted it.
int can be implicitly converted to double because there is a predefined implicit conversion, see here
There are many ways to convert or cast from type to type. For example, You can use implicit/explicit conversion, you can use TypeConverter or implement the IConvertible interface.
Now, you have to decide which use case is relevant to you. It can be a bit complex to check them all, especially without knowing the destination type at design time.
In your code snippet, type is a variable of type Type, hence why it is throwing that error. You can change your code to use Convert.ChangeType() instead. Something like this:
var value = Reader[item];
PropertyInfo property = properties.Where(x => x.Name == item).FirstOrDefault();
var type=property.PropertyType;
object cs= Convert.ChangeType(value, type);
if (cs!=null){
...
}
Notice that, since you don't know the strong type of your property at compile time, you still have to box it into an object type after changing its type. This means you wouldn't be able to access its properties and methods directly using dot syntax in code (e.g. cs.MyProperty). If you wish to be able to do that, you can use the dynamic type in C#:
dynamic dcs = cs;
Console.Write(dcs.MyProperty);
When using Convert.ChangeType() you have to make sure you are converting to the correct type. E.g.:
if (value.GetType() == type)
{
object cs= Convert.ChangeType(value, type);
}
I have a simple control method DoSomething which receives a json string, converts it to a dynamic object and tries to get the data:
[HttpPost]
public string DoSomething(string jsonStr) // {"0":null,"1":"loadData"}
{
// In this case obj.GetType() = System.Web.Helpers.DynamicJsonObject
object obj = Json.Decode(jsonStr);
// Correct, a == "loadData"
var a = (obj as dynamic)["1"];
// Incorrect, compilation exception:
// "Cannot apply indexing with [] to an expression
// of type 'System.Web.Helpers.DynamicJsonObject'"
var b = (obj as DynamicJsonObject)["1"]
return "OK";
}
My question is why it is possible to access an indexer when I use an object as a dynamic object at the time when the original type doesn't have an indexer?
In the first case, you're using all the infrastructure of dynamic - which doesn't just use reflection to find "real" members, but also uses IDynamicMetaObjectProvider to provide support for members which are only known at execution time. This works because when you're using dynamic, the process of binding (working out what a member name means) is performed at execution time instead of at compile time.
In this case, it's the indexer which is being used at execution time - DynamicJsonObject doesn't declare an indexer, but overrides the TryGetIndex method of DynamicObject. The meta-object provider implementation of DynamicObject will route indexer "get" calls via TryGetIndex.
A simpler example of this is ExpandoObject:
ExpandoObject foo = new ExpandoObject();
// This is invalid; the compiler can't find a Property member
foo.Property = "broken";
dynamic bar = foo;
// This is fine; binding is performed at execution time.
bar.Property = "working";
Why this simple code doesnt work?
var abc = new[]{1,2,3,4}.Select(x => default(typeof(x)));
i expect something like array of zeros but get compiler error. So how i can default values in lambda expressions?
In my real application i have meta class with type Type
public class FieldInfo
{
public Type type
}
I get IEnumerable<FieldInfo> as parameter to my method and want return an array of default values for each type (object[])
This is the problem:
typeof(x)
You're using the typeof operator as if it's a method call, taking a value. It's not. It needs a compile-time type name or type parameter (or unbound type such as Dictionary<,>, or void).
The same is try with the default operator.
So that's what's wrong - but without knowing what you're trying to achieve, we can't actually advise you on how to fix it. You might just want something like this:
public static IEnumerable<T> SelectDefault<T>(this IEnumerable<T> source)
{
return source.Select(x => default(T));
}
Then this:
var abc = new[]{1,2,3,4}.SelectDefault().ToArray();
... would give you an int[] with all values zero.
EDIT: Now we have more information, it's easier to provide what you want, which is basically a method to get the default value from a Type, boxing as appropriate:
public static object GetDefaultValue(Type type)
{
// Non-nullable value types should be boxed, basically
if (type.IsValueType && Nullable.GetUnderlyingType(type) == null)
{
// Not the swiftest approach in the world, but it works...
return Activator.CreateInstance(type);
}
// Everything else defaults to null
return null;
}
So you'd have something like:
var defaults = fields.Select(field => GetDefaultValue(field.type));
(Where field is a FieldInfo as per the question - I'm deliberately not calling GetType() here.)
Do you mean this?
var abc = new[]
{
1, 2, 3, 4
}
.Select(x = > {
var xType = x.GetType();
if (xType.IsValueType)
{
return Activator.CreateInstance(xType);
}
else
{
return null;
}
})
You can't get the default value at runtime this way, default() and typeof() are compile time features. But you can try this suggestion. It uses Activator.CreateInstance to get the default value for a value type at runtime.
The compiler needs a compile-time type:
var abc = new[]{1,2,3,4}.Select(x => default(Int32));
will generate all 0's. I have no idea what you are trying to achieve with this, though, because all this does is return an empty array with the default value.
Can I define an object-structure as a parameter to a method in the parameter declaration without having to create a type?
I am inspired by LINQ to SQL queries, where you are able to return a subset of your query-results in the form of a new object:
var query = from t in dc.Table select new { Foo = t.column };
What you're describing isn't possible. In the case of your Linq to Sql query, the C# compiler creates an anonymous type with a single property named Foo with the same type as t.column. Type inferencing is then used and the variable "query" is actually strongly typed to this anonymous type (which is what gives you intellisense goodness on this variable).
Using "var" as a parameter type isn't possible because the type of the parameter can't be inferred, it requires the calling expression to decide on the actual type of the parameter.
The best that you could do would be to use generics and iterate through properties:
public static void Print<T>(T obj)
{
Type type = typeof(T);
PropertyInfo[] properties = type.GetProperties();
foreach(PropertyInfo pi in properties)
{
Console.WriteLine(pi.Name + ": " + pi.GetValue(obj, null));
}
}
which gives you a "rudimentary" ability to use anonymous types (or any type for that matter) as a parameter.
Nope, you cannot declare an anonymous type as an input parameter, and you cannot return it, unless you return it as an object. See this blog post for a hacky workaround, if you want, but that's really still just boxing and unboxing the type back and forth, so it's not actually better than just treating it as an object.
How useful is a parameter to a function that you can't use directly? Function parameters should help document the function.