I'm writeing a generice method in C#:
private T GetMamConfigurations<T>(IDictionary<string, object> items,
MaMDBEntities maMDBEntities) where T : class
{
T geoConfigs = default(T);
if (typeStr.Equals("MamConfiguration", StringComparison.OrdinalIgnoreCase))
{
geoConfigs = (T)GetGeoConfigurationNumericFromDB(items, maMDBEntities);
}
else if (typeStr.Equals("ListOfMamConfiguration", StringComparison.OrdinalIgnoreCase))
{
geoConfigs = (T)GetGeoConfigurationsPercentageFromDB(items, maMDBEntities);
}
return geoConfigs;
}
GetGeoConfigurationNumericFromDB returns MamConfiguration
whereas
GetGeoConfigurationsPercentageFromDB returns IList<MamConfiguration>
and I get compliation error:
cannot cast expression of type MamConfiguration to type T
Why is that?
Is there any way to solve without forcinf the two methods to return IList<MamConfiguration> ?
Well, you can always placate the compiler by adding an object cast in the middle:
geoConfigs = (T)(object)GetGeoConfigurationNumericFromDB(items, maMDBEntities);
which will defer the type-check until runtime. However! There is no compiler way to do this otherwise, as it is never going to be happy with string tests like "MamConfiguration". Also, generics work well when the code is ... generic - i.e. does the same thing which each type. The code shown is the opposite of generic. It is non-generic code exposed through a generic API. It is always going to be messy inside. Personally I would try to avoid this usage in the first place.
Instead of using (T) to cast to T, you can use the as operator. as will return null if the conversion fails. If it succeeds, it will return your converted value.
geoConfigs = GetGeoConfigurationNumericFromDB(items, maMDBEntities) as T;
Related
I have the following generic static class which is being used in a Fluent API. It takes an input parameter and returns a wrapper class containing the parameter cast to the generic type.:
public static Foo<TOut> InputAs<TOut>(object parameter) {
var castParameter = parameter as TOut;
if(castParameter == null) {
throw new Exception("Invalid cast");
}
return new Foo<TOut>(castParameter);
}
The problem is that the castParameter == null check always returns null. What would be the correct way to cast the object using the TOut generic parameter as the new type?
Well, if parameter as TOut returns null, then the runtime type of parameter isn't TOut.
Don't forget that operator resolution is done at compile-time, so if you have cast operators defined, they will not be invoked here. If you do need that, you can use dynamic:
public static Foo<TOut> InputAs<TOut>(dynamic parameter)
{
return new Foo<TOut>((TOut)parameter);
}
This will allow runtime operator resolution, and will call your cast operator if one is available. For example, it will allow you to pass long, while expecting int.
However, you probably want to find a different way of what you're trying to do; dynamic can be very useful, but it can also make debugging quite a bit harder, and you lose almost all compile-time warnings and errors that can help you identify problems before they happen.
I have a generic method (see code below) and want to perform some action if T is a particular interface.
Obviously I could just do a typeof(T).IsAssignableFrom(IFoo) but reflection is comparatively slow so I want to avoid it if possible.
Now, I know I can't have another method with the a constraint because I'd get the ambiguous method error otherwise I'd just do that.
Is there a way of doing what I want?
Does the fact that I want to do this point to some architectural problem?
public T Load<T>(string name)
{
T result = LoadFromName<T>(name);
if(T is IFoo) // Obviously doesn't work as T is a type, not a variable
(result as IFoo).FooMethod();
return result;
}
But you have got a variable - result! What's wrong with
var asFoo = result as IFoo;
if (asFoo != null)
asFoo.FooMethod();
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 */ });
HI!
Here is my case: I have some value type which is wrapped into another type with appropriate implicit converters. If I cast wrapped type to an object and then try to get original value I can do that in two-step cast only.
If simplified my code is as follows:
public enum MyEnum : int
{
First,
Second
}
public class Test<T>
{
public Test(T val)
{
Value = val;
}
private T Value { get; set; }
public static implicit operator T(Test<T> m)
{
return m.Value;
}
public static implicit operator Test<T>(T m)
{
var res = new Test<T>(m);
return res;
}
}
static void Main()
{
object res = new Test<MyEnum>(MyEnum.First);
Console.WriteLine((MyEnum)(Test<MyEnum>)res);
Console.WriteLine((MyEnum)res);
}
First "Console.WriteLine" works OK. Second one fails.
Is there any way I can modify this behavior and get it working without double casting?
UPDATE 1
I must use object to value cast (in real application I have to cast ComboBox.SelectedItem property and I do not want to add extra property to ComboBox, because I'll have to change my UI interaction code everywhere).
UPDATE 2
Implicit conversions to and from System.Object are not allowed.
UPDATE 3
Updated my sample code to reflect the whole problem.
Don't use object that way. Write your first line like this instead:
Test res = new Test(1);
If you must have it in an object first, remember that all the compiler knows about it at this point is that it's an object, and nothing more. You, as the programmer, have additional information about what you expect this object to be, but for the compiler to take advantage of that information you have to put it into your code somewhere.
Update:
I'm glad I was able to find this again, because this almost-very-timely article by Eric Lippert, who works on the C# language design, went up this morning and explains the problem in depth:
http://blogs.msdn.com/ericlippert/archive/2009/03/19/representation-and-identity.aspx
If you want to simplify casting and not care performance effect, then create extension method.
public static T To<T>(this object obj) {
Type type = obj.GetType();
MethodInfo[] methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static);
MethodInfo method = methods.FirstOrDefault(mi => (mi.Name == "op_Implicit" || mi.Name == "op_Explicit") && mi.ReturnType == typeof(T));
if (method == null)
throw new ArgumentException();
return (T)method.Invoke(null, new[] { obj });
}
Usage
Console.WriteLine(res.To<MyEnum>());
Instead of adding implicit operators, consider implementing IConvertible. You only need to implement the ToInt32 method, the others are meaningless and you can throw the InvalidCastException in the other methods.
After that, you can use Convert.ToInt32() method to convert your object in one step.
or even
var res = new Test(1);
Your local variable res is always of type object; so the line that isn't working is trying to convert an object, that isn't an int, to an int, which can't be done. Same as this fails:
object d = 5.5d;
Console.WriteLine((int)d);
EDIT:
Perhaps a pattern that might help is something like this:
if (res.GetType() == typeof(Test))
{
Console.WriteLine((int)(Test)res);
}
else
{
Console.WriteLine((int)res);
}
It's a very localized solution to your problem, but perhaps it will work for you.
While the error is due to res being of type object, I would make the Test->int operator explicit...
The code below is looping through a dictionary of strings and IMyCompanySettings looking for values that implement IMyCompanyProductSetting. Clearly, trying to cast and raising an exception is a very expensive way to do this.
public static List<IMyCompanyProductSetting> GetProductSettings(ConfigurationManager cfm)
{
List<IMyCompanyProductSetting> ret = new List<IMyCompanyProductSetting>();
foreach(IMyCompanySetting setting in cfm.Values)
{
try
{
IMyCompanyProductSetting prod = (IMyCompanyProductSetting)setting;
ret.Add(prod);
}
catch
{
// Do nothing.
}
}
return ret;
}
What's a better way to do this?
Casting 101 [general info on casting stuff]:
Use [object] is [interface/class] expression:
if (setting is IMyCompanyProductSetting) {
...
}
Alternatively you can use the as keyword which tries to cast the object and if it fails, instead of throwing exception, it'll return null. Note that the target type must be a reference type in the as keyword:
var prod = setting as IMyCompanyProductSetting;
if (prod != null) {
...
}
You should always use the above code instead of the equivalent exception handling.
Filtering an IEnumerable by type (LINQy):
As Jon Skeet pointed out, you should use OfType extension method to filter a sequence easily (assuming you got LINQ):
var filteredSequence = sequence.OfType<TargetType>();
Casting an IEnumerable to type (LINQy):
If you want to try casting each element to the target type (as opposed to filtering by type), you can use the Cast extension method:
var castedSequence = sequence.Cast<TargetType>();
The "hard" way (pre-LINQ) is to use "as". This is more efficient than using "is" and then casting each time (as both the "is" and the cast require execution-time checks):
IMyCompanyProductSetting prod = setting as IMyCompanyProductSetting;
if (prod != null)
{
ret.Add(prod);
}
See another question for when to use "as" and when to use a cast.
If you're using .NET 3.5, however, it's really easy:
return cfm.Values.OfType<IMyCompanyProductSetting>().ToList();
Very easy :)
Mehrdad has the answer. I'll only add that you should never use that "try / catch everything" trick. At best in that case, you're trying to catch an InvalidCastException. You wouldn't want to ignore some other exception, possibly from the execution of the method you're trying to call.
You should use the 'Is' statement instead for more concise and less error prone code. See the example below.
if (setting Is IMyCompanyProductSetting)
ret.add((IMyCompanyProductSetting)setting);
Either do
if (setting is IMyCompanyProductSetting)
{
IMyCompanyProductSetting prod = (IMyCompanyProductSetting)setting;
}
or
IMyCompanyProductSetting prod = setting as IMyCompanyProductSetting;
if (setting != null)
{
}