|
|
Comments and Discussions
|
|
 |

|
You just saved me from having to create this myself. 5 and bookmarked.
|
|
|
|

|
[TestFixture]
public class ObjectCastTests
{
[Test]
public void TestObjectCastion()
{
double? x = typeof(double?).Cast(1.0);
int y = typeof(int).Cast(1.2345);
}
}
When I build this i get build errors
Error 209 Cannot implicitly convert type 'object' to 'double?'. An explicit conversion exists (are you missing a cast?)
Error 210 Cannot implicitly convert type 'object' to 'int'. An explicit conversion exists (are you missing a cast?)
What am I doing wrong?
|
|
|
|

|
Oops, I forgot to fix the example. There should be an explicit cast of a System.Object returned from Cast to the desired type. In this case, the cast will not do any real casting, only unboxing:
double? x = (double?)typeof(double?).Cast(1.0);
In production code based on the prototype from this article I added a generic wrapper to Cast, letting you write
double? x = typeof(double?).Cast<double?>(1.0);
|
|
|
|

|
good one - have 5 from me
|
|
|
|

|
I should say that your code has a very interesting thing... which also forbids you from doing all the mappings only once.
If you create a new type with implicit or explicit conversions, your cast will be able to use them. Really useful.
|
|
|
|
|

|
In your example you are doing simple value conversion. The following works just fine for me (accepting the loss of precious, of course):
float foo = 1F;
double? bar = foo;
double foo2 = 1D;
float? bar2 = (float)foo2;
int foobar = (int)bar;
int foobar2 = bar2.HasValue ? (int)bar2.Value : 0;
Needless to say, I am very confused by your solution. Josh Fischer
|
|
|
|

|
This is a solution for performing a CLR cast in situations when neither the target type nor the type of the value being converted is known statically (i.e. at compile time).
Consider a situation when you read a row from a database, and populate properties of a .NET object with its data. The query comes as a string passed to your program at run-time, and the object comes as an instance of System.Object.
The algorithm would go like this:
for each field returned from the query
1. Use reflection to get a property setter corresponding to the name of the query field
2. Get the value of the field from the SqlDataReader
3. Convert the data to the type of the corresponding property
4. Call the property setter through reflection, passing it the converted value
Step #3 is necessary if you want your solution to be general. Without this step, reading a SQL float into a .NET float would result in an exception, because SqlDataReader converts SQL floats to double, among other things.
|
|
|
|

|
O, I see now; the example threw me off. You have the makings of a great article here. I would add some more discussion, and examples, as to why the other options don't work. Also, put the example you gave me in your comment into the article; it makes your solution much more clear.
Two more questions:
1. Why are you multiplying the hash code by 31?
2. Do you know exactly why Expression.Convert works? What is going on under the hood that isn't going on with the other methods. The MSDN documentation didn't help me much, but I might be missing something (again)
I would also investigate what Paulo said; I think he might be right Josh Fischer
|
|
|
|

|
When I combine hash codes of multiple items, I multiply prior result by a small prime, usually 31, like this: 31*((31*(31*A+B))+C)+D, and so on. I do not remember why I do it like that - I think I saw it done this way somewhere, but I do not remember where
Expression.Convert does not do anything - it's simply a representation holder for something that can be compiled into a lambda expression at run-time. The interesting stuff happens in the Compile method of the Lambda - it produces a delegate corresponding to the expression of the lambda. In this case, it's three casts - object val to its unboxed type, then to the desired result type, and finally to a boxed object. Once compiled, this becomes a delegate that encapsulates the specific conversion logic, which relies on CLR to perform casts in the same way it does casts in the regular compiled .NET code.
I'll try adding more discussion and write better examples when I get a chance - right now, I' too swamped at work
Off-topic:
I have a whole library developed based on this trick - I parse textual representations of expressions, construct LINQ expressions from them, and put the results inside lambdas. Once I compile these lambdas, I get very fast, type-safe delegates that execute whatever logic that was encoded in the expression string that I parsed.
|
|
|
|

|
dasblinkenlight wrote: it produces a delegate corresponding to the expression of the lambda
I know how lambdas work, lol
dasblinkenlight wrote: perform casts in the same way it does casts in the regular compiled .NET code
This is what I wasn't seeing; it's so simple! (slaps head)
I put together a sample that makes it more clear; well to me anyway.
public class TestClass<T,U>
{
public T Test(U val)
{
return (T)typeof(T).Cast(val);
}
public object Test2(object val)
{
return typeof(T).Cast(val);
}
public T Test3(U val)
{
return (T)Convert.ChangeType(val, typeof(T));
}
public object Test4(object val)
{
return Convert.ChangeType(val, typeof(T));
}
}
TestClass<int?,int> nullTest = new TestClass<int?,int>();
var foo = nullTest.Test(5);
var foo2 = nullTest.Test2(5);
bool bar = foo2 is Nullable<int>;
int foo5 = (int)5D;
TestClass<int, double> cheat = new TestClass<int, double>();
int foo6 = cheat.Test(5D);
var foo7 = cheat.Test2(5D);
bool bar2 = foo7 is int;
dasblinkenlight wrote: I parse textual representations of expressions
I've actually been doing some work around this area too (that's why I'm so interested in understanding your solution). Needless to say, I am disappointed with 3.5's expression support, but 4.0 looks very exciting (assignments!).
There are also some great articles here on codeproject where people have parsed all kinds of text based expressions and converted them to every format you can imagine. One in particular spits everything directly into IL; crazy.Josh Fischer
|
|
|
|

|
| re: multiplying by 31, that helps insure that the low-order bits of all values get used. Check my blog for more http://bit.ly/9qNcga[^] http://musingmarc.blogspot.com
|
|
|
|

|
dasblinkenlight wrote: Consider a situation when you read a row from a database, and populate properties of a .NET object with its data
I would just avoid that situation. One simple way is for the class to have a factory method that accepts a DataReader.
|
|
|
|

|
I am certain that there are numerous situations when a factory method taking a DataReader provides an appropriate solution, but there are also situations when this is not going to work. This may happen for various reasons, the simplest of which is inability to change data classes from a library.
The particular problem that I solved with the code snippet from the article came up in programming a hierarchical in-memory data set, whose structure is entirely dictated by run-time configuration. The data set can be populated from multiple sources of different nature. Each source has its own type mappings, so I needed a converter that casts values without asking too many questions
|
|
|
|

|
It is interesting to see the use you gave to LINQ... but I must say that simple creating a new Convert method that checks for nullable types and, if the value is not null, get the T of the nullable type and calls the Convert.ChangeType will solve the problem and looks much more simple...
Something like:
if (value == null)
return null;
Type dataType = typeof(T);
if (dataType.IsGenericType && dataType.GetGenericTypeDefinition() == typeof(Nullable<>))
dataType = dataType.GetGenericArguments()[0];
return Convert.ChangeType(value, dataType);
|
|
|
|

|
LINQ-based solution is more than twice as fast
|
|
|
|

|
Are you sure about how fast it is? I didn't made the test, but consider:
Convert.ChangeType, as far as I know, is already implemented to be as fast as it can. But, you use a dictionary. Ok, when you only try int? to int, the dictionary lookup is very fast... but when you have already converted int -> double
double -> float
double ->int
int -> double?
int? -> double
And so on... it really keeps that advantage?
Also, the Synchronized attribute you used means locking. So, if you use a multi-core machine (say, 8 CPUs) the Convert.ChangeType will be able to run in all cores without problems (for example, when many threads are processing database-data), where the lookup to the dictionary will run one at a time (even if the conversion itself is faster) and will clear the CPU caches... I can't imagine it will keep that advantage.
But, as I said earlier, it is a very interesting use of the expressions trees. I think you will be one of the first to use the new expressions trees in .Net 4.0, where you can create full methods using them.
* As final notes, I think that if your PairOfTypes is a struct it will be even faster and Convert.ChangeType can convert to and from strings.modified on Thursday, February 25, 2010 4:41 PM
|
|
|
|

|
I preloaded the cache of compiled converters with some 80+ converters by doing nearly all possible pairs of types between which the conversion is allowed, but the running speed did not change considerably (1M casts completed in 0.379 vs. 0.370 when only one item was in the cache).
Your solution is some 40% faster (0.135 vs. 0.186) when the target type is not nullable. It seems that the reflection calls is what kills it: as I mentioned earlier, it is converting to a nullable that takes more twice the time (0.777 vs. 0.379).
The locking can be thrown away completely if you preload the cache with all possible pairs of types in a static initializer. The program runs slightly faster without synchronization, too.
I tried making PairOfTypes a struct - much to my surprise, the tests became measurably slower.
I think that converting to and from strings was bundled into Convert.ChangeType only for convenience. To me, it is a distinct operation: you either want to convert a value to another type, or you want to convert objects to and from strings, but you usually know which one of these two you are doing. I think that it's a pure nonsense that an API would gladly convert an int to a string, but not to a nullable int
As for the .NET 4.0, I can't wait: I have to jump through too many hoops with System.Linq.Expressions in 3.5 to overcome limitations of lambda expressions, e.g. the four parameter limit
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.
|
Converting between types in .NET.
| Type | Article |
| Licence | CPOL |
| First Posted | 25 Feb 2010 |
| Views | 17,798 |
| Bookmarked | 27 times |
|
|