esac
esac

Reputation: 24695

How to have a method return a specific type?

I have a method which returns the base type, I want an overload (preferably, but a new method would be fine) that returns a parent class instead, so that I could do something like:

DynamicClass cl = new DynamicClass(...);
var dict = cl.CreateObject<DynamicDictionary>("ClassName");
dict.CallAddOnMethod("test");

Here is the code I have tried, but I can't seem to get to DynamicDictionary

public sealed class DynamicClass : DynamicBaseClass
{        
    public DynamicObject CreateObject(params object[] args)
    {
        Debug.Assert(args.Length > 0);
        object handle = InvokeMethod(CreateObjectMethod, args);
        return new DynamicObject(bcAssembly, (string)args[0], handle);
    }

    public T CreateObject<T>(params object[] args)
    {
        Debug.Assert(args.Length > 0);
        object handle = InvokeMethod(CreateObjectMethod, args);
        DynamicObject item = new DynamicObject(bcAssembly, (string)args[0], handle)
        return (T)item; // cant do this?
    }
}

public class DynamicObject : DynamicBaseClass
{
    private const string CallMethod = "Call";

    public DynamicObject(Assembly assembly, string parentName, object classHandle)
        : base(assembly, parentName, classHandle)
    {
    }       

    public object Call(string methodName, params object[] paramList)
    {
        return InvokeMethod(CallMethod, paramList);
    }
}

public sealed class DynamicObject<T> : DynamicObject
{
    private const string CallMethod = "Call";

    public DynamicObject(Assembly assembly, string parentName, object classHandle)
        : base(assembly, parentName, classHandle)
    {
    }      
}

public sealed class DynamicDictionary : DynamicObject
{
    private const string AddOnMethod = "AddOn";

    public DynamicDictionary(Assembly assembly, string parentName, object classHandle)
        : base(assembly, parentName, classHandle)
    {
    }

    public int CallAddOnMethod(string name)
    {
        return (int)Call(AddOnMethod, name);
    }
}

Upvotes: 1

Views: 193

Answers (2)

David.Chu.ca
David.Chu.ca

Reputation: 38704

One alternative is to load codes, compile the codes and execute the compiled results dynamically. The following example uses a private string as a template code source (you may load it from a text file):

using Microsoft.CSharp;

public class MyTemplate : IDisposable {
   private const string _SourceCodeTemplate = @"
using System;
public class {0} {{ // {{0}} Class name
public {0} CreateInstance() 
{{
{0} instance;
//... some codes here to create instance
return instance;
}}
public {1} {2}() {{ // {{1}} method result, {{2}} method name
{1} result;
// some codes here for the result
return result;
}}
}}";

  public T CallInstanceMethod<T>(string className, string callMethod, 
    string callMethodResult, 
    params object[] paramList) {
    string codes;
    // build codes dynamically
    codes = string.Format(_SourceCodeTemplate, className, 
              callMethodResult, callMethod);

     CSharpCodeProvider codeProvider = new CSharpCodeProvider();
     CompilerParameters parameters = new CompilerParameters();
     parameters.GenerateExecutable = false;
     parameters.GenerateInMemory = true;
     parameters.IncludeDebugInformation = false;

     foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
     {
       parameters.ReferencedAssemblies.Add(asm.Location);
     }

     // compile the codes
     CompilerResults cr= codeProvider.CompileAssemblyFromSource(parameters, codes);
     var csInstance = cr.CompiledAssembly.CreateInstance(className);
     Type type = csInstance.GetType();
     var methodForInstance = type.GetMethod(callMethod);

     object value;
     value = _methodForInstance.Invoke(csInstance, paramList);
     return (T)value;
  }
}

Upvotes: 0

Rob
Rob

Reputation: 27367

In your template, you should require that the type is a subclass of your dynamic object:
I.e.

var blah_b = test<b>();
blah_b.Dump();
var blah_c = test<c>();
blah_c.Dump();

-

public T test<T>() where T : a
{
    var item = Activator.CreateInstance(typeof(T));
    return (T)(item);
}
public class a
{

}

public class b : a
{

}

public class c : a
{

}

Upvotes: 1

Related Questions