I am currently writing code for runtime post-processing management.
I would like to make a generic method to be more effective but I get 2 error messages:
CS0118: 'procType' is a variable but used like a type (at out parameter)
CS0119: 'Fog' is a type, which is not valid in the given context (when calling the method)
void SetPostProc<T>(T procType, string IOHID, bool defval) where T : VolumeComponent
{
byte value = Convert.ToByte(IOHandler.GetVal(IOHID, defval));
volumeProfile.TryGet(out procType EFX);
{
};
}
SetPostProc(Fog, "", false);
What am I doing wrong?
Thanks for your help in advance!
First of all, if Fog is really a type, and not a variable, then you're not using the proper way of calling the generic function. If the generic type is not obvious from the first parameter, then you have to explicitly specify it like this:
Fog myFogObject = ...;
SetPostProc<Fog>(myFogObject , "", false);
However, if the type of myFogObject is known at compile time, which it seems like it is in your case, you don't have to specify the generic type because the compiler will figure it out automatically:
Fog myFogObject = ...;
SetPostProc(myFogObject , "", false);
This should solve your second error (CS0119).
The second problem is that procType is a variable referring to an object of type T, and not a type. You have to call the TryGet function by passing in the generic type parameter T like this:
volumeProfile.TryGet<T>(out T EFX);
Depending on what you're trying to do with this code, I think you don't even need the T procType parameter, just having the T generic parameter should be enough:
void SetPostProc<T>(string IOHID, bool defval) where T : VolumeComponent
{
byte value = Convert.ToByte(IOHandler.GetVal(IOHID, defval));
volumeProfile.TryGet(out T EFX);
{
// ...
};
}
EDIT: If you still want to get the result of TryGet outside the SetPostProc function, you'll need to declare the first parameter of your function as an out parameter:
void SetPostProc<T>(out T procObj, string IOHID, bool defval) where T : VolumeComponent
{
byte value = Convert.ToByte(IOHandler.GetVal(IOHID, defval));
volumeProfile.TryGet(out procObj);
{
// ...
};
}
Related
I am working on being able to dynamically invoke an instantiation of a class dynamically at run time.
I have spent the better part of this morning searching Google for an answer but I am pretty green in this world so I am sure the answers make sense, but they do not to me.
public class MSD : IGBRule
{
public MSD(GoverningBodyRulesGet_Result GBRule, int UserID)
{}
The line error and the error are both below
object v = Activator.CreateInstance(Type.GetType("RulesEngine.Rules.MSD, RulesEngine.Rules"), UserID, GBRules);
System.MissingMethodException: 'Constructor on type 'RulesEngine.Rules.MSD' not found.'
If you want to create an object and pass arguments to the constructor, you will have to provide the arguments in the correct order, matching the order you have specified in your constructor. So in your case, you want to pass the rule before the user id:
var type = Type.GetType("RulesEngine.Rules.MSD, RulesEngine.Rules");
object v = Activator.CreateInstance(type, GBRules, UserID);
If you pass the constructor arguments directly to the CreateInstance method, you will have to be careful with common types as it is possible that you are accidentally selecting a different overload that doesn’t call the correct constructor. You can avoid that by passing an object array with the arguments you want to pass:
var type = Type.GetType("RulesEngine.Rules.MSD, RulesEngine.Rules");
object[] args = new object[] { GBRules, UserID };
object v = Activator.CreateInstance(type, args);
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.
Given the following function;
void SomeFunction<T>(...){
SomeOtherFunction<T>();
}
This works fine, but sometimes the function fails before T passed is an array type, but it mustn't be an array type. These functions have to do with JSON deserialization of a dictionary, but for some reason it doesn't accept the T array argument when the dictionary has only one entry.
In short, I want to do this
void SomeFunction<T>(...){
try {
SomeOtherFunction<T>();
} catch ( Exception e ){
SomeOtherFunction<T arrayless>();
}
}
I've tried a ton of stuff, and I realize the real problem is somewhere else, but I need to temporary fix this so I can work on a real solution in the deserializer. I tried reflection too using the following method;
MethodInfo method = typeof(JToken).GetMethod("ToObject", System.Type.EmptyTypes);
MethodInfo generic = method.MakeGenericMethod(typeof(T).GetElementType().GetGenericTypeDefinition());
object result = generic.Invoke(valueToken, null);
But that doesn't quite work either.
Thank you!
I am not really sure what you are trying to achieve here, but to get the type of the elements in an array, you have to use Type.GetElementType():
void SomeFunction<T>()
{
var type = typeof(T);
if(type.IsArray)
{
var elementType = type.GetElementType();
var method = typeof(Foo).GetMethod("SomeOtherFunction")
.MakeGenericMethod(elementType);
// invoke method
}
else
foo.SomeOtherFunction<T>(...);
}
If I follow you correctly, you want to call one of two generic functions depending on whether the type of the object is an array or not.
How about:
if (typeof(T).ImplementsInterface(typeof(IEnumerable)))
someFunction<T>();
else
someOtherFunction<T>();
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.
I am working in c# with .net 2.0 (i know its old)
Here is my setup:
struct propertyType
{
System.type type;
}
public class DataProperties
{
private Dictionary<propertyType, object> properties;
public void setProperty(propertyType key, object value)
{
if(value.getType == key.type) //make sure they are adding valid data
properties.add(key, value);
}
public T getProperty<T> (propertyType key)
{
return (T)properties[key];
}
}
Then in my class that needs to pull properties it would look like
//this would be declared somewhere else for use by multiple classes.
//but for example sake its here
propertyType hash;
hash.type = typeof(byte[]);
byte[] hashCode = DataSet.properties.GetProperty<hash.type>(hash);
Now the last line is the one that doesnt work, but I'd like to work. I think the problem is that it doesnt like having a variable as the Type. In actual use there will be many different PropertyType objects so I want a way to get the properties out and cast to the correct type easily.
Does anyone know if it is for sure that the variable as the type is the problem. But at compile time it will know what hash.type is so its not that its an unknown value at compile time.
No, it is an unknown value at compile-time, as far as the compiler is concerned. Just because you've set it in the previous line doesn't mean the compiler really knows the value.
You simply can't use variables to specify type arguments in C# generics, other than with reflection. A type argument has to be the name of a type or another type parameter - it can't be an expression which evaluates to a System.Type.
the problem is that it doesnt like having a variable as the Type
That's right. There's no way to do this that keeps compile-time safety. It's possible to use reflection to call the getProperty method with a generic type that you only know at run time, but generics don't gain you anything in this case.
Since you're only using generics to do a type cast, why not add an overload to getProperty that's not generic and returns object?
As Jon Skeet and others have said, this cannot be done without reflection. So, here's some reflection code that should get you on the right track:
Type myGenericParam = typeof(byte[]);
MethodInfo method = typeof(DataProperties).GetMethod("getProperty").MakeGenericMethod(new[] { myGenericParam });
DataProperties foo = new DataProperties();
byte[] result = (byte[])method.Invoke(foo, new[] { /*parameters go here in order */ });