how does IConvertible works - c#

I have the following code which I got from the internet. I am not sure about the use of IConvertible. MSDN says:
"This interface provides methods to convert the value of an instance of an implementing type to a common language runtime type that has an equivalent value."
Is it meant that, I can assign anything to IConvertible and while using it converts it to the implementing type?
For example, in below example I pass parameters as key-value pair where key is string and value is IConvertible.
SqlParameter object = new SqlParameter(param.Key, param.Value)
SqlParameter object takes key as string and value as depending on the type declared in the table or stored procedure. How does it exactly works here?
private SqlCommand GetCommand(string procedureName,
IEnumerable<KeyValuePair<string, IConvertible>> parameters)
{
foreach (var param in parameters)
{
command.Parameters.Add(new SqlParameter(param.Key, param.Value));
}
}

Related

Expression.Convert(..., someGenericType) throws ArgumentException when used with generic type

I have this method, using Expressions to create fields getters:
public static Func<object, T> CreateFieldValueGetter<T>(this Type declaringType, FieldInfo fieldToGet) {
var paramExp = Expression.Parameter(typeof(object));
// ArgumentException if declaringType describes generic-type:
var cast = Expression.Convert(paramExp, declaringType);
var body = Expression.Field(cast, fieldToGet);
return Expression.Lambda<Func<object, T>>(body, paramExp).Compile();
}
It works great until I give it a generic type like:
class DataErrorNotifyingViewModelBase<TErr> : ViewModelBase, INotifyDataErrorInfo
where TErr : struct, IConvertible, IComparable, IFormattable
{
// ...
}
This way:
var vm = new DataErrorNotifyingViewModelBase<MyErrorsTypeEnum> ();
var type = vm.GetType();
// ArgumentException:
var getter = type.CreateFieldValueGetter<PropertyChangedEventHandler>(type.GetField("PropertyChanged"));
This is the exception I get:
Exception thrown: 'System.ArgumentException' in System.Core.dll
Additional information: Type GuiHelpers.DataErrorNotifyingViewModelBase`1[TErr] is a generic type definition
although simple casting works:
var vm = new DataErrorNotifyingViewModelBase<PrintDialogError>();
var obj = (object) vm;
So how can I feed it with generic types? Am I limited to non-generic-types only?
Edit - solution:
Kaveh Hadjari caught it:
Passing t = typeof (Dictionary<T, int>) will raise ArgumentException, as t.GetGenericArguments()[0].IsGenericParameter is true (albeit t.GetGenericArguments()[1].IsGenericParameter is false!)
Passing the type t = typeof (Dictionary<int, int>) works fine, becuse no element of the t.GetGenericArguments() array has IsGenericParameter == true
A generic type is a template for many different specialized types, and at runtime theres a difference between the generic type and the "instanced" types. A possible reason the call to Expression.Convert might be failing could be you're providing it with the type of the generic version and not with a specialized version with type variables set.
Update: I imagine there's a good reason this method would never work with generic types. Consider the case if the type variable is used as type for a field in the generic class. Since the type size (reference, Boolean, short, int, long, etc) could be variable it would mean that it could offset the memory address of other fields in different specializations of the generic class in a variable way. How would you know in advance which field length thus address offset would be the case if all the variables where not set? You couldn't and therefor we can't determine the the address of the field we might want to create a getter for. The only solution would be to actually create a getter that would rely on using reflection on each object you call the getter with, which would incur higher costs than imagined and if you are happy with that solution you might be as well have a single method that gets the value of the field using reflection without actually creating these getters in the first place.
"Am I limited to non-generic-types only?"
No, of course not. The error message is clear, though not consistent with the code example you provided. You seem to be passing for the type the original generic type definition (i.e. with an unspecified value for the type parameter), not the constructed generic type (i.e. with a specified value for the type parameter).
Unfortunately, without a good, minimal, complete code example that reliably reproduces the problem, it's impossible to know precisely what you've done wrong. All I can say is that you did do something wrong. If you want more specific advice than that, please edit your post so that it includes a good code example.
For what it's worth, here's a complete code example that demonstrates your method working fine with a generic type:
class A<T>
{
public int field1;
public T field2;
public event EventHandler Event1;
}
class Program
{
static void Main(string[] args)
{
A<bool> a = new A<bool>();
Func<object, int> field1Getter =
CreateFieldValueGetter<int>(a.GetType(), a.GetType().GetField("field1"));
Func<object, bool> field2Getter =
CreateFieldValueGetter<bool>(a.GetType(), a.GetType().GetField("field2"));
Func<object, EventHandler> event1Getter =
CreateFieldValueGetter<EventHandler>(a.GetType(), a.GetType()
.GetField("Event1", BindingFlags.NonPublic | BindingFlags.Instance));
}
static Func<object, T> CreateFieldValueGetter<T>(Type declaringType, FieldInfo fieldToGet)
{
var paramExp = Expression.Parameter(typeof(object));
// ArgumentException if declaringType describes generic-type:
var cast = Expression.Convert(paramExp, declaringType);
var body = Expression.Field(cast, fieldToGet);
return Expression.Lambda<Func<object, T>>(body, paramExp).Compile();
}
}
The only wrinkle here is that to obtain the field for the event, you have to specify the BindingFlags appropriate to that field (in particular, it's non-public so the default search of GetField() won't find it). The code you showed does this incorrectly, but it doesn't explain the exception you're getting.

Reflection c# in a set property

Why can't I use the value word in a set property when i'm trying to get the type of the value?
set
{
Type t = value.GetType();
if (dictionaries[int.Parse(value.GetType().ToString())] == null)
{
dictionaries[int.Parse(value.GetType().ToString())] = new Dictionary<string,t>();
}
}
It doesn't recognize the word t in my Dictionary constructor.
what am I doing wrong? how can I solve it?
You cannot use values or names of types as generic type parameters. Use a method with a generic type parameter instead:
void SetDict<T>(T value)
{
Type t = typeof(T);
if (dictionaries[t.FullName] == null)
{
dictionaries[t.FullName] = new Dictionary<string,T>();
}
}
Instead of using the type name, you can also use the Type value as a key directly for dictionaries:
Dictionary<Type, Dictionary<string,T>> dictionaries;
You can call it without specifying the generic type parameter, because the compiler can infer the type. However, this works only for the static type, not for the runtime type. I.e. you must call the method with an expression of the right type and not through a base type like object.
SetDict("hello"); // ==> string type
SetDict(42); // ==> int type
object obj = "world";
SetDict(obj); // ==> object type, not string type!
Note: Generic type parameters allow you to create strongly typed specialized types and methods at compile time. The advantage of strong typing lies in the fact that the compiler and the IDE can give you information on a type and certify that your code is statically correct AT COMPILE TIME. Creating a generic type at RUNTIME has no advantage, as you won't be able to use its advantages at compile time (or design time, if you prefer). You can as well use a Dictionary<string, object> or the like.
Please see my answer on code review: Type-safe Dictionary for various types. Especially my update to the answer.
You can't use a Type variable when declaring a generic type, you have to use an actual type.
In other words, this won't work:
Type t = ....
var x = new Dictionary<string, t>();
Depending on your class, you could do this:
public class Something<T>
{
public T Value
{
...
set
{
... new Dictionary<string, T>();
}
}
}
but that's not quite the same.
You also got a different problem, this:
int.Parse(value.GetType().ToString())
will not work.
value.GetType().ToString()
will likely produce something like System.Int32 or YourAssembly.NameSpace.SomeType, not a number that can be parsed.
I think you need to take one step back, and figure out what you're trying to accomplish here.

C# Language: How to get type of bound but open Generic class?

Let's say I have such a generic class
public class XClass<T, U>
{
public void MethodA<V>(){}
}
How could I get the type of
XClass<int,>
not hard-coded, not limiting to MakeGenericType method as below.
------ detailed elaboration below using MakeGenericType ------
I can get the type of the unbound and open class "XClass<,>" and its open method:
var type = typeof(XClass<,>);
Console.WriteLine(String.Format("Type ZClass<,>: \t generic? {0} \t open? {1}"
, type.IsGenericType, type.IsGenericTypeDefinition));
var method = type.GetMethod("MethodA");
Console.WriteLine(String.Format("Method MethodA<>: \t generic? {0} \t open? {1}"
, method.IsGenericMethod, method.IsGenericMethodDefinition));
Also, I can get the type of full closed class
XClass <int, char>
and its close method:
var type = typeof(XClass<,>);
var method = type.GetMethod("MethodA");
var fullType = method.DeclaringType.MakeGenericType(new[]{typeof(int), typeof(char)});
Console.WriteLine(String.Format("Type ZClass<int,char>: \t generic? {0} \t open? {1}"
, fullType.IsGenericType, fullType.IsGenericTypeDefinition));
var fullTypeOpenMethod = fullType.GetMethod("MethodA");
var fullMethod = fullTypeOpenMethod.MakeGenericMethod(typeof(string));
Console.WriteLine(String.Format("Method MethodA<String>:\t generic? {0} \t open? {1}"
, fullMethod.IsGenericMethod, fullMethod.IsGenericMethodDefinition));
Now, How can I get the type of bound but open class
XClass<int, >
and its method?
var type = typeof(XClass<,>);
var method = type.GetMethod("MethodA");
Type [] types = new Type[2];
types[0] = typeof(int);
types[1] = null; // what shall i put here?
var halffullType = method.DeclaringType.MakeGenericType(types);
If I put types[1] as null, an ArgumentNullException exception will throw "Value cannot be null".
What should I do?
What you are proposing to do is impossible and also will not really help you.
Impossible because...
The documentation states (emphasis mine) that
Types constructed with MakeGenericType can be open, that is, some of
their type arguments can be type parameters of enclosing generic
methods or types.
This means that you cannot make a Type object representing XClass<int,>. What you can do is:
class Outer<TOuter>
{
class XClass<T, U> {}
}
In this situation, you can make a Type object representing Outer<TOuter>.XClass<int, TOuter>. But there needs to be an enclosing generic class.
Not useful because...
The documentation also states (referring to a similar example to the above) that:
A constructed type such as Base is useful when emitting code,
but you cannot call the MakeGenericType method on this type because it
is not a generic type definition. To create a closed constructed type
that can be instantiated, first call the GetGenericTypeDefinition
method to get a Type object representing the generic type definition
and then call MakeGenericType with the desired type arguments.
Which means that if you have
Type myType = ... // represents Outer<TOuter>.XClass<int, TOuter>
Then to get a Type for XClass<int, string> you would first need to call myType.GetGenericTypeDefinition() (thus losing the int information) and then call MakeGenericType to put it back in (along with the string type parameter). So it's like one step back and two steps forward.
Alternatives
You might want to consider storing the type parameter types for XClass in a separate data structure (e.g. a Type[]) for as long as not all type parameters are known to you, and then create the closed generic type in one go after you have collected all of them.
You can also package all this into a small helper class for convenience:
class GenericTypeDescription
{
private readonly Type openGenericType;
private readonly Type[] typeParameters;
public GenericTypeDescription(Type openGenericType)
{
// add checks for openGenericType actually being what it says here
this.openGenericType = openGenericType;
this.typeParameters = new Type[openGenericType.GetGenericArguments().Length];
}
public void SetTypeParameter(int index, Type type) {
// add error handling to taste
this.typeParameters[index] = type;
}
public Type ConstructGenericType() {
// add error handling to taste
return this.openGenericType.MakeGenericType(this.typeParameters);
}
}
No, this isn't possible.
See my similar question: Does .Net support curried generics?
This is possible, when you feed MakeGenericType with its own generic arguments.
var type = typeof(XClass<,>);
var method = type.GetMethod("MethodA");
Type[] types = new Type[2];
types[0] = typeof(int);
types[1] = type.GetGenericArguments()[1]; // Use the open parameter type
var openConstructedType = type.MakeGenericType(types);
This will populate openConstructedType with a Type of XClass<int,U>.
Note that the type will have ContainsGenericParameters, so it won't be constructable, and there's no way to populate the open parameters.
I don't think that this is possible without inheriting from the class.
What you seem to be trying is to basically do this via reflection:
typeof(XClass<int,>)
This would be half-closed... and only possible by inheritance AFAIK:
class XClassInt<U>: XClass<int, U> {}
This second code allows you to get typeof(XClassInt<>).BaseType which is kind of what you want. However, in that case the second type argument for XClass<,> is not null but actually U (the type argument coming from XClassInt<>).
See also this MSDN page.
Edit: Here's my testbed for this:
public class C1<A,B> {}
public class C2<B>: C1<int, B> {}
[...]
Type baseType = typeof(C2<>).BaseType;
WL(baseType);
WL(baseType.GetGenericArguments()[0]);
Type arg1 = baseType.GetGenericArguments()[1];
WL(arg1);
WL(arg1.DeclaringType);
WL(arg1.GenericParameterPosition);
WL(arg1.IsGenericParameter);
Running this yields:
C1`2[System.Int32,B]
System.Int32
B
C2`1[B]
0
True
However, as I said, I believe this is only possible because the base type is closed with the generic type argument of the open generic type C2.

C#: Convert String to DBType.AnsiStringFixedLength

I have a stored procedure. One of its input parameters is expecting a char(8). I try to convert a string "AAA" to this particular parameter type, which is a DBType.AnsiStringFixedLength.
object v = Convert.ChangeType("AAA", param.DbType.GetTypeCode());
// param is AnsiStringFixedLength
However, all I get is an exception: Input string was not in a correct format.
And the stack trace says: at System.Number.StringToNumber(String str, NumberStyles options, NumberBuffer& number, NumberFormatInfo info, Boolean parseDecimal) [...]
Why is System.Convert trying to convert a string into a number, even though the prodecure's parameter is expecting a char(8)? How do I solve this? I don't want to use one huge switch case mapping all SQL types to CLR types...
EDIT:
This is the code in question: (A generic method to call any MS SQL stored procedure)
using (SqlConnection conn = new SqlConnection(this.config.ConnectionString))
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = this.config.StoredProcedureName;
conn.Open();
SqlCommandBuilder.DeriveParameters(cmd);
foreach (SqlParameter param in cmd.Parameters)
{
if (param.Direction == ParameterDirection.Input ||
param.Direction == ParameterDirection.InputOutput)
{
try
{
string rawParam = param.ParameterName.Replace("#", "");
if (this.config.Parameters.ContainsKey(rawParam))
{
try
{
param.Value = Convert.ChangeType(this.config.Parameters[rawParam],
param.DbType.GetTypeCode());
}
catch(Exception oops)
{
throw new Exception(string.Format("Could not convert to '{0}'.", param.DbType), oops);
}
}
else
throw new ArgumentException("parameter's not available");
}
catch (Exception e)
{
throw;
}
}
}
cmd.ExecuteNonQuery();
}
}
The actual parameter values are provided by this.config.Parameters - all of them are strings. I iterate through SqlCommand's parameter list and set them accordingly. Converting the string values to the parameter's Sql type is necessary here, and as far as I can see, the Sql type is provided by param.DBType.
You seem to mix up some things here, or I don't get what you try to do. The DbType (an enumeration) inherits Enum and that implements IConvertible -> You can call GetTypeCode(). But - you are now calling Enum.GetTypeCode(), which returns the underlying type. If you didn't specify it (and DbType didn't) any Enum is backed by an int.
What are you trying to solve with the code anyway? Why would you want to change the type of a string if the parameter is a string (although with a fixed length)?
Looking at the question some more it seems even more odd. You have an object v (probably for value?) - what do you care about the type?
object v1 = "Foo";
object v1 = 42;
What is the difference for you? I guess you want to pass the values to something else, but - if you only reference the value as object you might still need to cast it.
Please update your question and explain what you really want to do, what you expect to gain.
Regarding the comment:
I'm using Convert.ChangeType(object
value, TypeCode typeCode), so it's not
really converting into an Enum/int. At
least that's what I thought...
See above: DbType.GetTypeCode() is not what you want. Try it, give me the benefit of the doubt: What do you expect to get from DbType.AnsiStringFixedLength.GetTypeCode()? What is the actual result, if you try it?
Now to your code: You try to set the SqlParameter.Value property to the "correct" type. Two things: According to the documentation you probably want to set the SqlParameter.SqlValue, which is the value using SQL types according to the docs. SqlParameter.Value, on the other hand, is the value using CLR types and allows to infer both DbType and SqlValue. Sidenote, implementation detail: The SqlParameter.SqlValue setter just calls the setter of SqlParameter.Value again...
I would expect that the ADO.NET stuff converts the value on its own, if at all possible. What error are you getting without jumping through this hoops?

How to generate an instance of an unknown type at runtime?

i've got the following in C#:
string typename = "System.Int32";
string value = "4";
theses two strings should be taken to generate an object of the specified type with the specified value...
result should be:
object o = CreateUnknownType(typename, value);
...
Int32 test = (Int32)o;
Is this what are you are thinking?
object result = Convert.ChangeType("4", Type.GetType("System.Int32"));
As stated, this is too broad and can not be solved generally.
Here are some options:
Type type = Type.GetType(typename);
object o = Activator.CreateInstance(type);
This will create an instance of the type that typename is describing. It calls the parameterless constructor of that type. (Downside: Not all objects have a parameterless constructor. Further, this does set the state of the object using value.)
Type type = Type.GetType(typename);
object o = Activator.CreateInstance(type, new[] { value });
This will create an instance of the type that typename is describing. It calls a constructor of that type that accepts one parameter of type string. (Downside: Not all objects have such a constructor. For example, Int32 does not have such a constructor so you will experience a runtime exception.)
Type type = Type.GetType(typename);
object o = Convert.ChangeType(value, type);
This will attempt to convert the string value to an instance of the required type. This can lead to InvalidCastExceptions though. For example, Convert.ChangeType("4", typeof(FileStream)) will obviously fail, as it should.
In fact, this last example (create an instance of type FileStream with its initial state determined by the string "4") shows how absurd the general problem is. There are some constructions/conversions that just can not be done.
You might want to rethink the problem you are trying to solve to avoid this morass.
Creating an instance of a type you know by name (and which should have a default constructor):
string typeName = "System.Int32";
Type type = Type.GetType(type);
object o = Activator.CreateInstance(type);
Parsing a value from a string will obviously only work for a limited set of types. You could
use Convert.ChangeType as suggested
by PhilipW
or maybe create a
Dictionary<Type,Func<string,object>>
which maps known types to known parse
functions
or use reflection to invoke the
Parse(string) method on the type,
assuming there is one:
string valueText = "4";
MethodInfo parseMethod = type.GetMethod("Parse");
object value = parseMethod.Invoke(null, new object[] { valueText });
or maybe you can use the
infrastructure provided by the .NET
component model. You can fetch the
type converter of a component and use
it like this:
TypeConverter converter = TypeDescriptor.GetConverter(type);
object value = converter.ConvertFromString(valueText);
Your logic seems a little flawed here. Obviously, if you're directly casting the object at a later time to the actual type it is, then you must know the type to begin with.
If there's something else that is missing from this question please elaborate and maybe there is a more appropriate answer than simply, "This doesn't make much sense."
Perhaps you have a set of different types, all of which implement a known interface?
For example if you have several different user controls and want to load one of them into a container, each one might implement IMyWobblyControl (a known interface) yet you might not know until runtime which of them to load, possibly from reading strings from some form of configuration file.
In that case, you'll need to use reflection to load the actual type from something like the full assembly name, then cast it into your known type to use it.
Of course, you need to make sure that your code handles invalid cast, assembly not found and any of the other exceptions that are likely to come along through something as wobbly as this...
This seems like a job for Int32.Parse(string). But to agree with the others it seems this is "unique" and one should probably think gloves.
Here's a specific example of the problem involving Azure SQL Federations...which splits data into separate db's according to a key range.
The key range types are:
SQL / .Net SQL type / CLR .Net
INT / SqlInt32 / Int32, Nullable
BIGINT / SqlInt64 / Int64, Nullable
UNIQUEIDENTIFIER / SqlGuid /Guid, Nullable
VARBINARY(n), max n 900 / SqlBytes, SqlBinary /Byte[]
Ideally, a C# function param could take either .Net SQL type or CLR .Net type but settling on just one category of type is fine.
Would an "object" type param be the way to go? And, is there a feasible way to identify the type and convert it accordingly?
The concept is something like:
public void fn(object obj, string fedName, string distName, bool filteringOn)
{
...figure out what type obj is to ensure it is one of the acceptable types...
string key = obj.toString();
return string.Format("USE FEDERATION {0} ({1}='{2}') WITH RESET, FILTERING = {3}", fedName, distName, key, (filteringOn ? "ON" : "OFF"));
}
Though the param value is cast to string, it will be recast/checked on the sql server side so validating it on the app side is desired.
After using:
Type type = Type.GetType(typename);
Try this extension method:
public static class ReflectionExtensions
{
public static T CreateInstance<T>(this Type source, params object[] objects)
where T : class
{
var cons = source.GetConstructor(objects.Select(x => x.GetType()).ToArray());
return cons == null ? null : (T)cons.Invoke(objects);
}
}
Hope this helps.

Categories