C# Recompile and reload static class duringruntime [duplicate] - c#

Currently I am working on a project in C# where I have to implement reflection. I have created a WPF application with a GUI. This GUI contains a
combobox which contains all the classnames that implement a specific interface. The classes with the displayed classnames live in the same solution.
Next to the combobox is a button to refresh the content in the combobox. However, when I run my application, modify a classname that implements the interface, and
click on that refresh button the changes don't show up in the combobox. For example, when I change a classname it should display the new classname in stead of the old one.
I have extracted this part of my project to test it in an empty console application. Here I have an interface that is implemented by the classes
QuickSortAlgorithm, DynamicSortAlgorithm and MergeSortAlgorithm. Next I wrote the following, straight forward code, in my main class.
public static List<string> AlgoList = new List<string>();
static void Main(string[] args) {
RefreshAlgorithms();
Print();
Console.WriteLine("\nChange a classname and press a key \n");
Console.ReadKey();
Print();
Console.WriteLine("\nPress a key to exit the program \n");
Console.ReadKey();
}
private static void RefreshAlgorithms() {
AlgoList.Clear();
Type AlgorithmTypes = typeof(IAlgorithms);
foreach (var type in Assembly.GetCallingAssembly().GetTypes()) {
if (AlgorithmTypes.IsAssignableFrom(type) && (type != AlgorithmTypes)) {
AlgoList.Add(type.Name);
}
}
}
private static void Print() {
Console.WriteLine("Algorithm classes:");
foreach (var Algo in AlgoList) {
Console.WriteLine(Algo);
}
}
When I run the application is see the classnames QuickSortAlgorithm, DynamicSortAlgorithm and MergeSortAlgorithm printed. However if I change the name of the, for example,
QuickSortAlgorithm class to QuickSortAlgorithmmmmm I would expect it to print QuickSortAlgorithmmmmm once I press a key. However this is not the case and the name
QuickSortAlgorithm is still being displayed.
I get the feeling that I overlook something in the concept of reflection. Can this even be done after building the solution? If I understood correctly this concept makes it possible to implement changes on runtime. I know that
it will make my application much slower but I'm really eager to learn more about this concept. If one can explain me what I'm doing wrong I would be very happy.

That unfortunately does not work. When your assembly gets loaded, it will stay loaded as it is, changes only applying when you restart your application.
If you are using .NET Framework you can create a new AppDomain and load your assembly into this AppDomain. When you are done, you can unload the AppDomain and with it your assembly. That you can do multiple times in a running application.
void RefreshAlgorithms()
{
var appDomain = AppDomain.CreateDomain("TempDomain");
appDomain.Load(YourAssembly);
appDomain.DoCallback(Inspect);
AppDomain.Unload(appDomain);
}
void Inspect()
{
// This runs in the new appdomain where you can inspect your classes
}
Be careful though, as working with AppDomains has caveats, such as the need to use remoting when communicating with the AppDomain.
In .NET Core there is no such way available, as far as I know

Once you load a compiled .NET assembly into your application, you can't make further changes to the types in that assembly without restarting and rebuilding the application. If this were allowed, then it could lead to all kinds of weird behavior. For example, imagine if the application had a List<Foo> populated with 3 foos and then Foo.Id were changed from an int to a string. What should happen to that live data?
However, if your application doing the reflecting is different than the assembly being reflected over, it is possible to set things up such that you can watch for changes to that assembly file and re-do your reflection. The key is to abandon System.Reflection (which only works on loaded assemblies) and instead use the Mono.Cecil library.
Cecil reads in the assembly metadata without loading the code into the application, so it works well for the "reflection-only" use-case. Of course, what it can't do is actually invoke the code. The Cecil API contains many similarities to System.Reflection. For example:
var assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly(Path.Combine(projectDirectory, "bin", "Debug", "Something.dll"));
var controllerTypes = assembly.MainModule.Types.Where(t => t.BaseType?.FullName == "System.Web.Mvc.Controller")
.ToArray();
Another note is that .NET Framework (not .NET Core) contains the notion of AppDomains which can be loaded an unloaded. These act like .NET "sub-processes" within the one process and have rules about what can cross their boundaries. If you really need to both reload code and execute it, then this could be a solution.
Another option could be the Roslyn scripting API, which would work well if you want to dynamically load and execute source code (vs. compiled assemblies).

It looks like you're overlooking one small step: building your code. Once you rename the class to QuickSortAlgorithmmmm, you need to save and build that assembly.
Doing so will recreate the assembly (assuming your application doesn't have an open handle on it). After that, clicking the refresh button should show the new name.
If you can't reload the assembly because it has your GUI code in it too (which is running), you may want to separate out the classes that implement the interface into their own assembly, potentially build that separately, and copy it over into a directory where your app can find it (eg. in a Plugins directory).

Related

Refactoring-friendly way to use EditorAttribute in cross-platform app

My cross-platform app (win & mac) consist of three assemblies:
Core - platform independent (TargetFramework = net6.0), no additional dependencies
Platform.Win - WinForms UI and other Windows-specific services (TargetFramework = net6.0-windows + winforms). References Core.
Platform.Mac - Mac-specific UI and other services (TargetFramework = net6.0-macos). References Core.
For simplicity let's put Platform.Mac aside and consider only Core + Platform.Win.
In my app a WinForms PropertyGrid is widely used to allow user to edit objects. There are also a lot of custom UITypeEditors exists to help user with editing.
public class ClassToEdit
{
[Editor(typeof(MyUiEditor), typeof(UITypeEditor))]
public PropType Prop { get; set; } = new();
}
This works perfectly when both ClassToEdit and MyUiEditor belongs to the Platform.Win assembly.
But if I want to move ClassToEdit into the Core, this will not work because MyUiEditor & UITypeEditor are undefined in the Core.
I definitely can specify types for EditorAttribute as strings:
//[Editor(typeof(MyUiEditor), typeof(UITypeEditor))]
[Editor("Platform.Win.MyUiEditor, platform.win",
"System.Drawing.Design.UITypeEditor, System.Windows.Forms")]
public PropType Prop { get; set; } = new();
And this works fine as soon as platform.win assembly is loaded at runtime.
But this is totaly not refactoring-friendly. VS will not show the usage of MyUiEditor and will not handle class rename/move/other edits.
Furthermore, there is also no compilation-time checks that specific type exists and so on, so if class was rernamed or removed, the app will compile and run sucessfully until a real attempt to edit ClassToEdit with PropertyGrid will take place. In this moment app will crash with unhandled exception saying MyUiEditor not found or something similar.
I found, that I can use TypeDescriptor.AddAttributes to add EditorAttribute for types in runtime, so I can remove EditorAttribute from ClassToEdit declaration and add the following in Platform.Win startup code
TypeDescriptor.AddAttributes(typeof(PropType),
new EditorAttribute(typeof(MyUiEditor),
typeof(UITypeEditor)));
I also found that I can use the similar (but more complicated) approach not only for types, but for properties (How to add property-level Attribute to the TypeDescriptor at runtime?).
But the idea of separation of ClassToEdit and its editor declarations not looks easy-to-maintain to me (especially because I will need to maintain two versions - win & mac - of such editor declarations in separate assemblies).
So, I wonder if there is better refactoring-friendly way to use EditorAttribute in cross-platform code?

C# Reflection - How to reload a class on runtime?

Currently I am working on a project in C# where I have to implement reflection. I have created a WPF application with a GUI. This GUI contains a
combobox which contains all the classnames that implement a specific interface. The classes with the displayed classnames live in the same solution.
Next to the combobox is a button to refresh the content in the combobox. However, when I run my application, modify a classname that implements the interface, and
click on that refresh button the changes don't show up in the combobox. For example, when I change a classname it should display the new classname in stead of the old one.
I have extracted this part of my project to test it in an empty console application. Here I have an interface that is implemented by the classes
QuickSortAlgorithm, DynamicSortAlgorithm and MergeSortAlgorithm. Next I wrote the following, straight forward code, in my main class.
public static List<string> AlgoList = new List<string>();
static void Main(string[] args) {
RefreshAlgorithms();
Print();
Console.WriteLine("\nChange a classname and press a key \n");
Console.ReadKey();
Print();
Console.WriteLine("\nPress a key to exit the program \n");
Console.ReadKey();
}
private static void RefreshAlgorithms() {
AlgoList.Clear();
Type AlgorithmTypes = typeof(IAlgorithms);
foreach (var type in Assembly.GetCallingAssembly().GetTypes()) {
if (AlgorithmTypes.IsAssignableFrom(type) && (type != AlgorithmTypes)) {
AlgoList.Add(type.Name);
}
}
}
private static void Print() {
Console.WriteLine("Algorithm classes:");
foreach (var Algo in AlgoList) {
Console.WriteLine(Algo);
}
}
When I run the application is see the classnames QuickSortAlgorithm, DynamicSortAlgorithm and MergeSortAlgorithm printed. However if I change the name of the, for example,
QuickSortAlgorithm class to QuickSortAlgorithmmmmm I would expect it to print QuickSortAlgorithmmmmm once I press a key. However this is not the case and the name
QuickSortAlgorithm is still being displayed.
I get the feeling that I overlook something in the concept of reflection. Can this even be done after building the solution? If I understood correctly this concept makes it possible to implement changes on runtime. I know that
it will make my application much slower but I'm really eager to learn more about this concept. If one can explain me what I'm doing wrong I would be very happy.
That unfortunately does not work. When your assembly gets loaded, it will stay loaded as it is, changes only applying when you restart your application.
If you are using .NET Framework you can create a new AppDomain and load your assembly into this AppDomain. When you are done, you can unload the AppDomain and with it your assembly. That you can do multiple times in a running application.
void RefreshAlgorithms()
{
var appDomain = AppDomain.CreateDomain("TempDomain");
appDomain.Load(YourAssembly);
appDomain.DoCallback(Inspect);
AppDomain.Unload(appDomain);
}
void Inspect()
{
// This runs in the new appdomain where you can inspect your classes
}
Be careful though, as working with AppDomains has caveats, such as the need to use remoting when communicating with the AppDomain.
In .NET Core there is no such way available, as far as I know
Once you load a compiled .NET assembly into your application, you can't make further changes to the types in that assembly without restarting and rebuilding the application. If this were allowed, then it could lead to all kinds of weird behavior. For example, imagine if the application had a List<Foo> populated with 3 foos and then Foo.Id were changed from an int to a string. What should happen to that live data?
However, if your application doing the reflecting is different than the assembly being reflected over, it is possible to set things up such that you can watch for changes to that assembly file and re-do your reflection. The key is to abandon System.Reflection (which only works on loaded assemblies) and instead use the Mono.Cecil library.
Cecil reads in the assembly metadata without loading the code into the application, so it works well for the "reflection-only" use-case. Of course, what it can't do is actually invoke the code. The Cecil API contains many similarities to System.Reflection. For example:
var assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly(Path.Combine(projectDirectory, "bin", "Debug", "Something.dll"));
var controllerTypes = assembly.MainModule.Types.Where(t => t.BaseType?.FullName == "System.Web.Mvc.Controller")
.ToArray();
Another note is that .NET Framework (not .NET Core) contains the notion of AppDomains which can be loaded an unloaded. These act like .NET "sub-processes" within the one process and have rules about what can cross their boundaries. If you really need to both reload code and execute it, then this could be a solution.
Another option could be the Roslyn scripting API, which would work well if you want to dynamically load and execute source code (vs. compiled assemblies).
It looks like you're overlooking one small step: building your code. Once you rename the class to QuickSortAlgorithmmmm, you need to save and build that assembly.
Doing so will recreate the assembly (assuming your application doesn't have an open handle on it). After that, clicking the refresh button should show the new name.
If you can't reload the assembly because it has your GUI code in it too (which is running), you may want to separate out the classes that implement the interface into their own assembly, potentially build that separately, and copy it over into a directory where your app can find it (eg. in a Plugins directory).

Dynamic localization of messages

I have made a simple localization of messages. All messages are stored in the static class Lng
public static partial class Lng
{
public static readonly string AppName = "My application";
public static class Category1
{
public static readonly string ConfirmDelete = "Are you sure want to delete?";
}
}
In code usage is as simple as referencing fields
MessageBox.Show(Lng.Category1.ConfirmDelete, ...
Then there is a manager, which does following:
language selection
load corresponding translation
updating fields via reflection
export currently selected language on application exit for an update (in case if default language is selected - to create first translation for any other language)
It's irrelevant of how language files looks likes, but here is a reflection part
TranslateLng("Lng.", typeof(Lng));
...
private static void TranslateLng(string parent, Type type)
{
foreach (Type nested in type.GetNestedTypes())
{
string child = string.Format("{0}{1}.", parent, nested.Name);
TranslateLng(child, nested);
foreach (var field in nested.GetFields())
{
string key = child + field.Name;
DefaultAdd(key, (string)field.GetValue(null)); // store value in default language dictionary (if not created yet)
field.SetValue(null, GetValue(key)); // get value for currently selected language
}
}
This system has one problem: all messages are defined in one class, which required manual management (deleting and updating messages when updating code which uses them).
And I was thinking to change manager to register strings dynamically and simplify usage to something like
MessageBox.Show(Lng.Text("Are you sure want to delete?"), ...
So that text is defined right where it used, duplicated text can be handled by manager and so on.
There are however 2 problems:
I will need a complete list of all messages at the end of application run to export complete list of messages (for currently selected language). What if some of Lng.Text() are never called at that run? Is there a way to register them as they are used in code (compile time?)? So that all calls will be registered somehow, even if peace of code is never used.
How to generate key. I could use CallerMemberName, but right key are more useful, as they are telling exact purpose. To example, Lng.Configuration.Appearance.CaptionText. I could call Lng.Text(key, message), but then I have to manage keys, ensure in their uniqueness, which doesn't appeals me.
I recently worked on a project with internationaliztion and we used Resources in con junction with the Sisulizer program with great success. Having the resources solves your key problem as you manually enter the key when you extract the resources. You also get great support from Resharper which makes the whole process a breeze.
Sisulizer is then used to extract resources as well as strings hard-coded in our Win Forms and WPF classes. It can export a CSV which you can give your translators and it also supports pseudo translation, which makes testing such apps very easy as well.

How to reflect over assemblies that are already loaded?

I have 2 projects in the solution. Project1UI references Project2Reports
Project1UI:
MainForm.cs
Project2Reports:
BaseReport.cs // all classes below inherit from it
Report1.cs
Report2.cs
Report3.cs
From Project1UI, how can I find all the classes that inherit from BaseReport? The project1UI already references the 2nd assembly - is there a way to do it without manually loading the 2nd assembly manually (e.g. Assembly.Load) since it's already loaded.
You have to process all the types in the assembly and look for the types that implement it.
You can use something like that (written by hand right now, it may contains errors).
foreach (Type type in Assembly.GetAssembly(typeof(BaseReport)).GetTypes())
{
if (type != typeof(BaseReport) && typeof(BaseReport).IsAssignableFrom(type))
{
// we found a type, we can store it somewhere, for example, in a list and our list in a static readonly field for fast lookup in the future.
myreports.Add(type);
}
}
You can also process all the loaded assemblies.
This however is not the best way to do that, is complicated, quite obscure and quite hard to understand.
I would use a simple factory class that will give you the instance of your report as requested, when you add a report, add it through a simple .Add call.

Programatically generating assemblies from OWL files with ROWLEX

I have been using the ROWLEX library to handle RDF-s. It is shipped with a designtime GUI tool called OwlGrinder.exe that can generate C# helper classes (.NET assemblies to be exact) from my OWL ontologies. I wonder if anyone knows if I could do the same programatically in runtime.
ROWLEX just became open source, so now you have the chance to actually look inside the code of OwlGrinder.exe and copy the code from there. However, here is a short example:
private NC3A.SI.Rowlex.AssemblyGenerator generator;
private void RunAssemblyGeneration(XmlDocument ontologyFileInRdfXml)
{
this.generator = new NC3A.SI.Rowlex.AssemblyGenerator();
this.generator.GenerateAsync(ontologyFileInRdfXml, "myAssemblyName",
null, this.OnGenerationFinished);
}
private void OnGenerationFinished(string errorMessage)
{
if (errorMessage == null)
{
// Success
// Displaying warnings and saving result
string[] warnings = this.generator.Warnings;
this.generator.SaveResult(#"C:\myAssemblyName.dll");
// Important! One generator instance can be executed only once.
this.generator = null;
this.RejoiceOverSuccess();
}
else
{
// Failure
this.MournOverFailure();
}
}
If you want to generate assemblies in runtime, I assume that you might want to repeat that over and over again as your user demands. You have to watch out here, because .NET does not allow you to unload an assembly. Therefore you cannot get rid of the assemblies from your previous runs. The solution is that you execute the generation code every time in a new AppDomain which can be unloaded. OwlGrinder.exe does exactly this, you might want to peak inside the MainForm.cs
Yes, Mr Lame, you can programmatically generate .NET code.
There are a couple options.
Create the code as text.
You can compile any .cs or .vb source file from within an app. See the help for the Microsoft.CSharp.CSharpCodeProvider class, for a starter. You invoke the compiler programmatically, specifying the resources to embed, where to put the generated assembly, the dependencies, and so on. One scenario here is using a template.cs file, embedding a little more code into it, and then compiling it. The result is an assembly (.dll or .exe or .netmodule if you like) resulting from that code. You can then load that assembly and call into it, using reflection.
Create the code using a document object model.
The relevant feature area here is called "CodeDom" and it works like the HTML DOM for web pages, except the document object model is used to create .NET code. Programmatically you construct the code, using DOM elements.
example of the CodeDom thing:
var class1 = new System.CodeDom.CodeTypeDeclaration(className);
class1.IsClass=true;
class1.TypeAttributes = System.Reflection.TypeAttributes.Public;
class1.Comments.Add(new System.CodeDom.CodeCommentStatement("This class has been programmatically generated"));
// add a constructor to the class
var ctor= new System.CodeDom.CodeConstructor();
ctor.Attributes = System.CodeDom.MemberAttributes.Public;
ctor.Comments.Add(new System.CodeDom.CodeCommentStatement("the null constructor"));
class1.Members.Add(ctor);
// add one statement to the ctor: an assignment
// in code it will look like; _privateField = new Foo();
ctor.Statements.Add(new System.CodeDom.CodeAssignStatement(new System.CodeDom.CodeVariableReferenceExpression("_privateField"), new System.CodeDom.CodeObjectCreateExpression(fooType)));
// include a private field into the class
System.CodeDom.CodeMemberField field1;
field1= new System.CodeDom.CodeMemberField();
field1.Attributes = System.CodeDom.MemberAttributes.Private;
field1.Name= "_privateField";
field1.Type=new System.CodeDom.CodeTypeReference(fooType);
class1.Members.Add(field1);
etc etc. You can add regular methods, all sorts of statements in the code, and so on. AFAIK the CodeDom stuff supports everything the language supports. You can do lambdas and linq expressions, conditionals and control flow, anything.
You can then compile that class, and again produce an assembly that you can save to disk or keep in memory and load dynamically.

Categories