0

I'm writing a program that would have the possibility to learn for itself.

Example:

  1. a word 'day' was typed
  2. look for interface named 'day'
  3. does it exists?
  4. No => create class based on that interface (and implement the interface) and save it for next use then create instance of that class
  5. Yes => create instance of that class

Google gave me this: How to dynamically create a class in C#?

This solution is the closest I can think of for my scenario, but it assumes you already know how many properties you will need and doesn't implement an interface at all.

I have no experience at all with system.reflection but I'm eager to learn!

Anyone know an example for my case?

Any help is appriciated.

Thank You!

EDIT: MY SOLUTION (Because nobody gave me a straight answer I figured it out myself)

    public void createObject(string name)
    {
        //Namespace where the interfaces are located
        string strnamespace = "Intelligence.Omnia.Categories";
        //Get interfacecollection
        List<Type> interfaceCollection = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsInterface && t.Namespace == strnamespace).ToList();
        foreach (Type myinterface in interfaceCollection)
        {
            //interface names
            List<string> interfaceNames = new List<string>();

            if (myinterface.Name == name)
            {
                //Add interface name
                interfaceNames.Add(myinterface.Name);

                //Add current interfaceproperties
                List<PropertyInfo> myProps = myinterface.GetProperties().ToList();

                //Does the current interface inhiretes from other interfaces?
                foreach (Type inhiretences in myinterface.GetInterfaces())
                {
                    //Add interface name
                    interfaceNames.Add(inhiretences.Name);

                    //Add those properties aswell!
                    foreach (PropertyInfo pi in inhiretences.GetProperties())
                    {
                        myProps.Add(pi);
                    }
                }

                createType(name, myProps, interfaceNames);
            }
        }
    }
    static void createType(string name, List<PropertyInfo> props, List<string> interfacesnames)
    {
        //create instance of CSharpCodeProvider
        CSharpCodeProvider csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v4.0" } });

        //DLL
        string pathDLL = AppDomain.CurrentDomain.BaseDirectory + "Objects.dll";
        CompilerParameters parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.dll", "System.Linq.dll", "System.Threading.Tasks.dll", "Intelligence.dll" });
        parameters.OutputAssembly = pathDLL;
        parameters.GenerateExecutable = false;
        ICodeCompiler icc = csc.CreateCompiler();

        //>>>>Generated CODE

        //Add namespaces
        CodeCompileUnit compileUnit = new CodeCompileUnit();
        CodeNamespace ns = new CodeNamespace("Objects");
        compileUnit.Namespaces.Add(ns);
        ns.Imports.Add(new CodeNamespaceImport("System"));
        ns.Imports.Add(new CodeNamespaceImport("System.Collections.Generic"));
        ns.Imports.Add(new CodeNamespaceImport("System.Linq"));
        ns.Imports.Add(new CodeNamespaceImport("System.Text"));
        ns.Imports.Add(new CodeNamespaceImport("System.Threading.Tasks"));
        ns.Imports.Add(new CodeNamespaceImport("Intelligence"));
        ns.Imports.Add(new CodeNamespaceImport("Intelligence.Omnia"));
        ns.Imports.Add(new CodeNamespaceImport("Intelligence.Omnia.Categories"));

        //Define your class
        CodeTypeDeclaration classType = new CodeTypeDeclaration("Object"+name);
        classType.Attributes = MemberAttributes.Public; //make it public
        foreach(string interfaceName in interfacesnames) //let it inherit from the interfaces
        {
            classType.BaseTypes.Add(interfaceName);
        }
        ns.Types.Add(classType);

        //Add constructor
        CodeConstructor constr = new CodeConstructor();
        constr.Attributes = MemberAttributes.Public;
        classType.Members.Add(constr);


        //Add all the properties
        foreach (var prop in props)
        {
            //If you want private fields
            //CodeMemberField field = new CodeMemberField(prop.PropertyType, prop.Name);
            //classType.Members.Add(field);

            CodeMemberProperty property = new CodeMemberProperty();
            property.Attributes = MemberAttributes.Public | MemberAttributes.Final;
            property.Type = new CodeTypeReference(prop.PropertyType);
            property.Name = prop.Name;
            property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), prop.Name)));
            property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), prop.Name), new CodePropertySetValueReferenceExpression()));
            classType.Members.Add(property);
        }

        //Write the file for later use
        TextWriter tw = new StreamWriter(new FileStream(AppDomain.CurrentDomain.BaseDirectory + "Objects\\" + name + ".cs", FileMode.Create));
        csc.GenerateCodeFromCompileUnit(compileUnit, tw, null);
        tw.Close();

        //Compile the class
        CompilerResults results = icc.CompileAssemblyFromFile(parameters, AppDomain.CurrentDomain.BaseDirectory + "Objects\\" + name + ".cs");
        results.Errors.Cast<CompilerError>().ToList().ForEach(error => System.Windows.Forms.MessageBox.Show(error.ErrorText));

    }
Community
  • 1
  • 1
Hades
  • 865
  • 2
  • 11
  • 28
  • 1
    So it would rebuild itself as well? Roslyn might have something for you, but in a compiled language this seems impossible. – BradleyDotNET Jun 02 '14 at 22:57
  • 1
    Have you tried .net's `dynamics`? – Stefan Jun 02 '14 at 23:00
  • Why the close flags? I think this a simple enough question and answer - or am i missing something that makes this too broad? – Preet Sangha Jun 02 '14 at 23:10
  • @BradleyDotNET 1) Who is Roslyn? 2) no it doesn't have to rebuild as it could just instantiate a new instance from it, like in the example in the link in my post. Basicly I need the same but based on a interface rather than pre-assumed user input. – Hades Jun 02 '14 at 23:11
  • I didn't VTC, seems like a reasonable question with a good succinct answer (granted I thought it was impossible, but thats not a VTC reason). 1) Roslyn is a new technology that enables a lot of runtime C# modification (I'm not that familiar with it). 2) Yes it does, even in the answer it compiles the code. You can't just take a cs file and instantiate it. – BradleyDotNET Jun 02 '14 at 23:13
  • @BradleyDotNET I'm pretty sure that instatiating a csfile is possible :D. It's just creating a csfile based on an interface that is a problem. – Hades Jun 02 '14 at 23:30
  • @Eli what specifically is the problem with creating from an interface? If you limit you main program to simply be the orchestration of dynamically constructed/loaded types I don't see the issue. – Preet Sangha Jun 02 '14 at 23:52
  • I'm going to vote to reopen, but I think the question needs to be rephrased to make it clearer that the OP is not asking how to write an artificial intelligence. At the moment it seems to be asking not just how to make a new class part of your program, but how to automatically write the code for implementing that class. – Harry Johnston Jun 08 '14 at 04:18

1 Answers1

3

As @Stefan says you can dynamically create type using the DLR built into .net 4.0+ but you can also use the older reflection based mechanism in CodeDom. My suggestion is to look at IronPython as this can do what you want really easily. However if you want C#, then you need to understand that C# is compiled and you need to use the compilation in System.CodeDom.Compiler.

Nothing can infer knowledge from word Day to what the interface Day is - you have to supply this is some way. However if you know the rules by which the interface will exist, you can create it dynamically. You can also go further and create additional types based on the interface. Again no code can be written magically - you need to supply the code semantics and syntax.

However if you have this you could separate the code into multiple assemblies (CompileAssemblyFromSource). Then dynamically load the assemblies to load the types (step 2). You can create types and assemblies at runtime (see OS: Generating DLL assembly dynamically at run time).

This code is from the above linked SO answer, and shows you how to an assembly from some string of code.

using System.CodeDom.Compiler;
using System.Diagnostics;
using Microsoft.CSharp;

CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler icc = codeProvider.CreateCompiler();
System.CodeDom.Compiler.CompilerParameters parameters = new CompilerParameters();
parameters.GenerateExecutable = false;
parameters.OutputAssembly = "My_Assembly_Day.dll";
CompilerResults results = icc.CompileAssemblyFromSource(parameters, ".... some C# code ....");

This SO answer shows you how to load assemblies to discover types:

https://stackoverflow.com/a/14184863/30225

edit: To answer comments.

  1. You can name your assembly anything you like - my suggestion is to name it with interface/class combination. The above is just an example of how you can name it
  2. You can either load the existing (pre created assemblies) at load time (first step in the program), or you can dynamically load the assemblies at runtime when needed.
  3. Once you've loaded the assemblies the types are available to you (Activator.CreateInstance) for example.
  4. If you get to your step and for instance type IDay is not available, you can dynamically create it using type text. Of course to get the CodeDom compiler to work, you'll need to ensure that all the things that this code references is supplied to the compilation unit. And compile that into an assembly on the disk. You can create the interface & the class that same time or in two steps.
  5. Once step 4 is done you can load that assembly like in step 3 & 4.

at the end of the day the answer boils down to:

  1. Find the existing type from existing types set
  2. If existing type is not available create it in an assembly dynamically.
  3. Load that type
  4. Instantiate that type.

The concern comes when using that type. You can either create more code using CodeDom (in effect this gives you the ability to recreate classes that may already exist BTW) or you can use types dynamically in your code. The first has the compilation overhead, while the second has the complexity in writing code that doesn't have hard coded types - a job made very simple btw using the C# dynamic keyword. Either way this is a very run of the mill .net coding technique when using types dynamically, and many existing applications use techniques like this for managing plugins.

Caveat:

Please remember that the smallest unloadable unit in .net is the AppDomain. When you load assemblies you load them into an AppDomain. If you want unload assemblies (and thus types) to replace them fo instance, you need to unlaod the AppDomain. This means that you need to ensure that any dynamically loaded assemblies are loaded into new AppDomains which in turn can be unloaded when needed.

Community
  • 1
  • 1
Preet Sangha
  • 64,563
  • 18
  • 145
  • 216
  • If I understand correctly this will generate a classlibrary AutoGen right? But this still means I need to manually give the code in a string instead of letting it derive from an interface. Plus will the program automaticly reference it(even on next startup)? – Hades Jun 02 '14 at 23:19
  • @Eli I've answered your comment in the question. – Preet Sangha Jun 02 '14 at 23:27
  • Can you remove the on hold thingy? – Hades Jun 03 '14 at 19:40
  • I've voted to reopen - but that's all I an do. Other people think this too broad - but I don't. – Preet Sangha Jun 03 '14 at 20:10