Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Dynamics: Convert.ChangeType versus Cast

Could someone explain why casting a dynamic object as a class returns that class while using Convert.ChangeType returns a dynamic object, particularly at runtime? For instance:

 dynamic dObject = new SomeClass();
 var dTest1 = dObject as SomeClass;                           // returns SomeClass
 var dTest2 = Convert.ChangeType(dObject, typeof(SomeClass)); // returns dynamic

The broader problem: I have a series of helper classes which implement a generic interface. I need to pass a list of these classes around to other objects; however the different helper classes use different types for the generic parameter, so I cannot pass a list of the helper classes directly:

interface IHelper<T>
{
    IEnumerable<T> Foo();
}
var HelperList = new List<IHelper<T>> // Can't do this because T varies from helper to helper!

So I thought I could fake out the runtime by creating a container class which contains the helper class and the generic type, but leverages dynamics:

class ContainerClass
{
    IHelper<dynamic> HelperClass;
    Type dType;                      // Specifies the type for the dynamic object
}

Now I can create and pass a List of ContainerClass around. All the processing works great until I need to assign the results from Foo() back to the destination IEnumerables, at which point I get runtime errors saying that type object cannot be converted to such and such concrete class, even though the unboxed object types match those required. If I attempt similar syntax as in dTest2 above, the runtime is still unable to figure out the "conversion".

I realize this is probably an abuse of dynamic and poor programming practice to boot. I will certainly use a different solution if and when I can identify one, but for now I either need to make this work or go with something less ambitious.

like image 428
tek Avatar asked Jan 12 '14 13:01

tek


People also ask

What C is used for?

C programming language is a machine-independent programming language that is mainly used to create many types of applications and operating systems such as Windows, and other complicated programs such as the Oracle database, Git, Python interpreter, and games and is considered a programming foundation in the process of ...

What is the full name of C?

In the real sense it has no meaning or full form. It was developed by Dennis Ritchie and Ken Thompson at AT&T bell Lab. First, they used to call it as B language then later they made some improvement into it and renamed it as C and its superscript as C++ which was invented by Dr. Stroustroupe.

Is C language easy?

C is a general-purpose language that most programmers learn before moving on to more complex languages. From Unix and Windows to Tic Tac Toe and Photoshop, several of the most commonly used applications today have been built on C. It is easy to learn because: A simple syntax with only 32 keywords.

Is C programming hard?

C is more difficult to learn than JavaScript, but it's a valuable skill to have because most programming languages are actually implemented in C. This is because C is a “machine-level” language. So learning it will teach you how a computer works and will actually make learning new languages in the future easier.


2 Answers

At execution time, there's no such thing as dynamic really.

However, the call to Convert.ChangeType is providing a dynamic value as an argument. Any method call using a dynamic argument is treated as having a return value of dynamic, because the compiler doesn't know what the actual signature will be until execution time.

However, if you use a cast, an is or an as expression, or a constructor call there's only one type that the result can be - so that's the type of the expression.

As for your broader problem - it's not clear to me that using dynamic would particularly help you. You may want to declare a base interface for IHelper<T> - a non-generic interface, that is only ever used for actual IHelper<T> instances. Then you can have a List<IHelper> where every element is actually an IHelper<T> for some T, but with T varying across instances. The IHelper interface isn't really required here, although if your IHelper<T> interface really contains other members which don't use T, those could be moved to IHelper instead. However, just having it for clarity can be useful.

Now, when you need to use a specific IHelper, then dynamic typing could briefly be useful. You can declare a generic method, and let dynamic typing figure out the type argument at execution time. For example:

private readonly IList<IHelper> helpers;

...

public void UseHelpers()
{
    foreach (dynamic helper in helpers)
    {
        UseHelper(helper); // Figures out type arguments itself
    }
}

private void UseHelper<T>(IHelper<T> helper)
{
    // Now you're in a generic method, so can use T appropriately
}
like image 180
Jon Skeet Avatar answered Sep 19 '22 15:09

Jon Skeet


Based on Jon Skeet's answer I think you can do something really interesting and avoid the dynamic keyword because it has a performance impact.

Use IHelper<T> : IHelper. Now you can store the helpers like into a List<IHelper>. Now you could call the Foo method by maping types to a generic method.

public IEnumerable<T> UseHelper<T> (IHelper<T> helper)
{

}

delegate IEnumerable<object> UseHelperDelegate(IHelper helper)
Dictionary<Type, UseHelperDelegate> helpersMap;

helpersMap.Add(typeof(int), UseHelper<int>); // Add others if you want

public IEnmerable<object> UseHelperWithMap(IHelper helper)
{
    Type helperType = helper.GetType();
    IEnumerable<object> retValue;
    if (helpersMap.Contains(helperType))
    {
         retValue = helpersMap[helperType](helper);
    }
    else // if the type is not maped use DLR
    {
         dynamic dynamicHelper = helper;
         retValue = UseHelper(dynamicHelper)
         // I wonder if this can actually be added to the map here
         // to improve performance when the same type is called again.
    }
}

Note: you can cast IEnumerable<SomeClass> to IEnumerable<object> and UseHelper<SomeClass> to UsehelperDelegate because of Covariance and Contravariance.

Edit: It turns out you can actualy create a new concrete function from the generic and add it to the map. This way you can avoid using dynamic.

var useHelperGeneric = this.GetType().GetMethods().FirstOrDefault(
               m=> m.IsGenericMethod && m.Name == "UseHelper");
var useHelper = useHelperGeneric.MakeGenericMethod(new Type[] { helper.GetType() });
var newFunction = (UserHelperDelegate)useHelper.MakeDelegate(typeof(UseHelperDelegate));

helpersMap.Add(helper.GetType(), newFunction);

newFunction(helper);
like image 36
dburner Avatar answered Sep 20 '22 15:09

dburner