7

This is similar to a few other threads i have found, but I haven't found the answer I need yet. I would appreciate a direct answer, even if it is "no, you can't do that".

Is there a way to use one block of code if a class/type exists and another if it doesn't. The result would be the same as using preprocessor directives but without the need to #define and manually comment or un-comment something in a file.

This may be a special use-case. I'm not sure. I'm working in an environment where sets of files can be installed, or not, before anything is compiled. So someone could buy a plugin which gets "installed" (files added to the project) which makes classes/types available for use (like extending an API). I need to provide a workaround if someone doesn't have one of our other plugin packages. I hope that makes sense.

It just wouldn't be user-friendly to ask someone to open up one of our files, if they have another plug-in, to un-comment a preprocessor directive, unless we have to.

e.g. I know this doesn't work because it only tests boolean if #define is used, but it illustrates what I am trying to do...

#if SomeType
    SomeType.DoSomething();
#else
    DefaultWay.DoSomething();

EDIT: I added this as a C# feature suggestion. Please vote here: http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2297494-add-type-testing-as-preprocessor-directive

I don't see how reflection would be able to do this, but I am new to C#, so examples using relection, if it is possible, would be great.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Rafe
  • 137
  • 1
  • 8

3 Answers3

5

Instead of using pre-compiler statements (which I don't know if they would work anyway if the client didn't have to recompile after installing your plug-in), I would suggest querying the assembly and trying to instantiate an instance of the class by string as seen here:

C# - Correct Way to Load Assembly, Find Class and Call Run() Method

Assembly assembly = Assembly.LoadFile(@"C:\dyn.dll"); 
Type     type     = assembly.GetType("TestRunner");  

if (type != null)
    //Do Something

Editing to show Activator call

if type is not null then use this to create an instance of the type you want.

var      obj      = Activator.CreateInstance(type); 
Community
  • 1
  • 1
Rikon
  • 2,688
  • 3
  • 22
  • 32
  • This looks very promising. I found I can get the current assembly using GetExecutingAssembly (untested, there are other options). The problem is, even if I get the type as a variable, intellesnse can't recognize members because the type is System.Type and i can't declare the type because I have to check it first...chicken of the egg. I think the only way forward would be to also use reflection to call the method I need but this is a performance sensitive area. I'll test with singleton and see how it goes, then come and edit this. – Rafe Oct 05 '11 at 17:19
  • @Rafe, I think I'm a little confused right? You're calling a type you expect to be there if they've loaded a specific assembly from plug in right? If the assembly reference isn't null and the type isn't null you can assume they've loaded that assembly and can create an instance through Activator that does what you need... I've edit'ed my answer to show the activator call... – Rikon Oct 05 '11 at 17:38
  • I marked this as the answer because it is the best that can be supplied without becoming situational. Unfortunately I won't be going down this road to supply my own solution because the effort cost to create this is not worth the benefit. For us this would be nice to have but is not critical. It should however be made clear the the simple "Do something" should be replaced by "lots more reflection work goes here". Even if going one level deep to a static method is another step. Going deeper in to instances is quite a bit more code depending on what you need. – Rafe Oct 05 '11 at 17:42
  • At Rikon (my 'at' symbol is getting stripped!): Yes, sort of... Actually, in my very specific case I'm trying to run a method which is stored in a static dictionary, so MyType.Dict["name"].TheMethod(3args). If this isn't there I do something else. Static access should be a little less code actually but you'll get the same issue with the activator, which is you have to use reflection all the way down. In your example, if obj had a method 'MyMethod' you would not be able to type obj.MyMethod() since obj is of type System.Type, not the actual type as defined in the original code. – Rafe Oct 05 '11 at 17:49
2

You could define interfaces for your types/services that your evaluation-provided code supports, but doesn't provide. Then you could use a plugin framework like MEF, which is built into the .Net Framework (v4.0).

MEF will do the reflection and assembly enumeration for you. You just have to define simple extension points in your code.

Here is some basic documentation for MEF. It might be specific to the Codeplex version of the code (not sure) but it shouldn't be too old, and should give you a good idea of how it works:

http://mef.codeplex.com/wikipage?title=Guide&referringTitle=Documentation

Alternative ideas

You might want to solve this with licensing rather than distribution.

You're going to have to solve the licensing problem anyhow, so you can collect money from users, and so you can sue people who grievously violate your copyright.

If your code is worth distributing, you won't be able to prevent distribution. Piracy is not preventable.

And most licensed code I've used recently have full-featured but timed trials, and phone home. They install all the code, but simply disable parts of it if they aren't licensed. It is hard for someone to know if they want to pay for your advanced features if they can't try them out :)

Merlyn Morgan-Graham
  • 58,163
  • 16
  • 128
  • 183
  • I'm not sure how MEF would work here. We don't have control over the application, only a piece of code someone can use in their own code, all of which is used in the application (this is a game engine if it matters)... Distribution in this case has no real installation. To simplify, you would just get some files to add to a project just as if you had written the code. If the file is there, the type is there. We do declare licensing but it is rather simple in nature. You can use it if you don't redistribute, etc. We don't even worry about the dishonorable few in the community. – Rafe Oct 05 '11 at 16:42
  • @Rafe: You said "files" in your OP, which I took to mean assemblies. Nearly all non-open-source .Net projects are distributed as assemblies, so you definitely have a special case here. That's fine and well, but that's why I didn't say more than just "use MEF" :) If you are distributing the *plugins* as assemblies (the accepted answer will only work if you are), then you could use MEF. You'd distribute an assembly that contains just the plugin interfaces, use those interfaces to make extension points in your source files, and implement those interfaces with your plugins. – Merlyn Morgan-Graham Oct 05 '11 at 19:19
  • 1
    @Rafe: It is up to you if MEF provides enough benefit to warrant using it. You're going to have to define a contract between the plugin and the code anyhow, and it will be easier to write code against an interface than it will against `object`. This will also give good perf (just virtual function calls once the type has been loaded). Other solutions involve you either calling with extended use of reflections (every call, property access, etc), using `dynamic` to do the same, or possibly compiling dynamic methods (see: https://github.com/ninject/ninject/wiki/How-Injection-Works) – Merlyn Morgan-Graham Oct 05 '11 at 19:28
  • 1
    @Rafe: Also, http://msdn.microsoft.com/en-us/library/system.reflection.emit.dynamicmethod.aspx – Merlyn Morgan-Graham Oct 05 '11 at 19:31
  • Thank you very much for all this information. I just can't justify all the time it would take to learn this stuff for a "nice to have" feature using code I'll probably never need again. I don't see why C# doesn't have this as a preprocessor directive. It would make it all so simple. I'd rather make a training video to un-comment that #define. If intellesense can figure out if a type exists, and provide feedback, in realtime, then this should be no problem for a compiler! Hopefully this all helps the next guy out. – Rafe Oct 05 '11 at 21:35
  • @Rafe: I believe they tried to limit the preprocessor in general because implementing features in it is a slippery slope. You could define a `#if HasPlugin` preprocessor definition instead. – Merlyn Morgan-Graham Oct 05 '11 at 21:40
0

Do you really care what is present at compile-time, or at run-time? You might be able to use a Factory pattern to encapsulate the logic for which class to instantiate assuming that polymorphism is possible (they both share an interface or base class).

Dylan Smith
  • 22,069
  • 2
  • 47
  • 62
  • Yes, compile time, if I am using the terminology right.It needs to work with intellisense, for example. – Rafe Oct 05 '11 at 16:45