Rewrite a method of a .Net DLL - c#

I have an old .NET DLL which I have lost the source code to and I wanted to see if I could change its behavior.
It has a method that takes in a string and returns a string.
I would want to rewrite this method such that I can return
startStr + " test";
I tried .NET reflector but the project it produced has a bunch of strange errors so I cannot recompile it.
I then tried Reflexil, but it only offers assembly level changes.
Is there any way I could rewrite the method in C# and have the DLL use my method instead?

Reflexil should be able to handle this for you - you need to switch to IL view in Reflector and then you can go to the method that you want to change, pull up Reflexil and make your changes. This avoids the problems with decompiling the assembly to source code (which never worked for me without errors in Reflector).
If all you want to do is append a string to a string variable, you can just do something like:
// assuming your original string is already on the stack
ldstr " test"
call System.String System.String::Concat ( System.String, System.String )
This will create a new string on the stack with test appended to it. Once you're done with the editing, you can save the assembly back to disk.
If you need something more complicated (like appending a string returned by a function call), you simply need to call the right method and then call Concat() on the two strings on the stack.
If your original method returns a string then wrapping it in a new function in a new assembly may be a better solution, though. I'd only edit the IL if you really need the original assembly to change because - for example - the string returned from the particular method is used within that same assembly and you need other functions in that assembly to see the changed return value.
Note: I used Reflector 6.8.2.5 and Reflexil 1.0 for this - current Reflector / Reflexil may be different. Let me know if you need these files for your changes.

Have you tried extension methods? Simply add another method to an existing class.
You do this by:
public static class Foo {
public static String SomeMethod (this Bar bar) {
return bar.OriginalMethod()+" test";
}
}
If the original class was Bar.

I've never used .NET reflector, but you can try using the free decompiler offered by JetBrains, the makers of ReSharper, it has never failed me. http://www.jetbrains.com/decompiler/
On a side note, since you say that the dll is very old, couldn't the compilation errors in the Reflector generated project be produced by different versions of references or targeted framework?

Related

Source code for the "nameof" operator of C#

Where can I get the source code for "nameof" of C# or how do I decompile it?
I checked https://referencesource.microsoft.com/, but I couldn't find it.
It's not something you can decompile as such, or show you source code for. It's part of the C# compiler: when you use nameof(Foo) the compiler just injects "Foo" into the source code. The IL for the methods is exactly the same:
static void PrintMyName()
{
Console.WriteLine(nameof(PrintMyName));
}
vs
static void PrintMyName()
{
Console.WriteLine("PrintMyName");
}
As noted in comments, it's not just that the name is taken literally as per the operand; it's the last part of the name that's used. So for example, if you have:
string x = "10";
string text = nameof(x.Length);
then that will resolve to "Length". (This doesn't use the value of x at execution time, either - it's fine if x is null. Or you could use nameof(string.Length) or nameof(String.Length).)
nameof is a keyword, so you would need to look into the compiler for the source code of how it is processed. Fortunately for you, the C# compiler is now open-sourced under the Roslyn project. Understanding a compiler is not a trivial task – source code is passed through pipelines of transformations, which each one adding more syntactic or semantic information. To start you off, the GetContextualKeywordKind parses the nameof keyword into a SyntaxKind.NameOfKeyword, which then gets matched in TryBindNameofOperator.
As to your other question of creating another such operator: Yes, you can, by cloning and modifying the Roslyn source. However, your new operator would obviously only work on the modified compiler, so you'd need to supply this to whoever will be compiling your code. This is something that's rarely done; you're normally better off defining extension methods for your custom functionality, unless you need something particularly esoteric.

Using F# quotations to detect code changes

I need to cache results from heavy calculations done by several different classes inheriting from the same base class. I was doing run-time caching by subclass name. Now I need to store the results on disk/DB to avoid long recalculations when I restart my app, but I need to invalidate cache if I change the code inside Calculate().
type BaseCalculator() =
let output = ConcurrentDictionary<string, Series<DateTime, float>>() // has public getter, the output is cached by the caller of Calculate() method
abstract Calculate: unit->unit
type FirstCalculator() =
inherit BaseCalculator()
override this.Calculate() = ... do heavy work here ...
From this question and answers I have learned that I could use [<ReflectedDefinition>] on my calculate method. But I have never worked with quotations myself before.
The questions are:
Could I use a hash code of quotations to uniquely identify the body of the method, or there are some guids or timestamps inside quotations?
Could [<ReflectedDefinition>] be applied to the abstract method and will it work if I override the method in C#, but call the method from F# runner? (I use reflection to load all implementations of the base class in all dlls in a folder)
Is there other simple and reliable method to detect code changes in an assembly automatically, without quotations? Using last modified time of an assembly file (dll) could work, but any change in an assembly will invalidate all calculators in the assembly. This could work if I separate stable and WIP calculators into separate assemblies, but more granularity is preferred.
Could I use a hash code of quotations to uniquely identify the body of the method, or there are some guids or timestamps inside quotations?
I think this would work. Have a look at FsPickler (which is used by MBrace to serialize quotations). I think it can give you a hash of a quotation too. But keep in mind that other parts of your code might change (e.g. another method in another type).
Could ReflectedDefinition be applied to the abstract method and will it work if I override the method in C#, but call the method from F# runner?
No, the attribute only works on F# methods compiled using the F# compiler.
Is there other simple and reliable method to detect code changes in an assembly automatically, without quotations?
I'm not sure. You could use GetMethodBody method and .NET reflection to get the IL, but this only gives you the immediate body - not including e.g. lambda functions, so changes that happen elsewhere will not be easy to detect.
A completely different approach that might work better would be to keep the calculations in FSX files in plain text and compile them on the fly using F# Compiler Service. Then you could just hash the source of the individual FSX files (on a per-computation basis).
I have found that Mono.Cecil is surprisingly easy to use, and with it I could hash all member bodies of a type, including base type.
This SO question was very helpful as a starting point.

How to return two values from F# to C#

I have an F# function returning a string and an array to some C# code. I tried a few ways and did some searching to no avail. How is this done? My F# function (Gen.Best) ends with:
(T1 Best).ToString,T2 Best // return a (string) and a (float []) to C#
F# shows the return type as (unit -> String) * float []
(T1 Best) is a (float -> float) function.
In my C# code I have:
Tuple<string, double[]> Ans = Gen.Best(XData, ResultData);
The compiler complains:
The type 'Microsoft.FSharp.Core.Unit' is defined in an assembly that is not referenced. You must add a reference to assembly 'FSharp.Core, Version=4.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
C:\Users\JDL\Dropbox\Formulas\VS Bogdan\Quirkle\MainForm.cs 139 13 Quirkle
How do I add a reference to the assembly? (I'm new to F#)
and
Cannot implicitly convert type 'System.Tuple<Microsoft.FSharp.Core.FSharpFunc`2<Microsoft.FSharp.Core.Unit,string>,double[]>' to 'System.Tuple<string,double[]>'
C:\Users\JDL\Dropbox\Formulas\VS Bogdan\Quirkle\MainForm.cs 139 46 Quirkle
You need to add a reference to the assembly in your C# project, not your F# project. Of course, FSharp.Core is referenced by default in an F# project. This page explains how: https://msdn.microsoft.com/en-us/library/7314433t%28v=vs.90%29.aspx
The other problem is that you forgot the parentheses in your ToString call. This line:
(T1 Best).ToString,T2 Best
should be this instead:
(T1 Best).ToString(),T2 Best
The code you wrote returns a tuple whose first element is not the result of calling ToString(); it is the ToString method itself, represented as an F# function of type unit -> string.
It's not really clear from your question what problem you're having. Taking a guess: if you simply compile your F# code as a managed DLL assembly and reference it from your C# code (making your function public, of course), you'll find your function implemented as returning an instance of System.Tuple<string, float[]>. This works just fine with C# code.
If that doesn't address what you're asking, please edit your question with additional details to make precisely clear what it is you are asking. Please see https://stackoverflow.com/help/how-to-ask for more information on how to improve your question.
EDIT:
With respect to your edits:
Assuming that the missing reference is actually in your C# assembly, then you add a reference to the FSharp.Core.dll assembly the same way you'd usually do it. E.g. right-click the "References" item in your C# project, choose "Add Reference...", make sure "Assemblies" is selected, and then find the desired assembly in the list (you can type "FSharp.Core" in the search box to make it easier to find the desired assembly).
Note that in general, you may find it easier and more productive to only expose standard .NET types from your F# assembly. That way, C# code using your F# library doesn't need to reference the .NET F# assemblies, nor do anything special to convert (if needed).
In this case, that would mean using the standard .NET System.Func<1, TResult> type instead of the FSharpFunc<'T, 'U> type.
See this comment in a related answer for corroboration of my suggestion. :)
The error message regarding the conversion from one tuple type to another relates to your question (in the comment below) about how to convert a function to a string. Without a clearer question – including a good, minimal, complete code example and a precise description of what you actually intend for the code to do – I don't see a way to answer that part of the question.

How to translate or convert CompilerGenerated code?

If you try to use decompilers like: jetbrains dotpeek, redgate reflector, telerik justdecompile, whatever.. Sometimes if you need a code to copy or just to understand, it is not possible because are shown somethings like it:
[CompilerGenerated]
private sealed class Class15
{
// Fields
public Class11.Class12 CS$<>8__locals25;
public string endName;
// Methods
public Class15();
public bool <Show>b__11(object intelliListItem_0);
}
I'm not taking about obfuscation, this is happens at any time, I didsome tests (my own code), and occurs using lambdas and iterators. I'm not sure, could anyone give more information about when and why..?
So, by standard Visual Studio not compile $ and <> keywords in c# (like the code above)...
There is a way to translate or convert this decompiled code automatically?
Lambdas are a form of closure which is a posh way of saying it's a unit of code you can pass around like it was an object (but with access to its original context). When the compiler finds a lambda it generates a new type (Type being a class or struct) which encapsulates the code and any fields accessed by the lambda in its original context.
The problem here is, how do you generate code which will never conflict with user written code?
The compiler's answer is to generate code which is illegal in the language you are using, but legal in IL. IL is "Intermediate Language" it's the native language used by the Common Language Runtime. Any language which runs on the CLR (C#, vb.net, F#) compiles into IL. This is how you get to use VB.Net assemblies in C# code and so on.
So this is why the decompilers generate the hideous code you see. Iterators follow the exact same model as do a bunch of other language features that require generated types.
There is an interesting side effect. The Lambda may capture a variable in its original context:
public void TestCapture()
{
StringBuilder b = new StringBuilder();
Action l = () => b.Append("Kitties!");
}
So by capture I mean the variable b here is included in the package that defines the closure.
The compiler tries to be efficient and create as few types as possible, so you can end up with one generated class that supports all the lambdas found in a specific class, including fields for all the captured variables. In this way, if you're not careful, you can accidentally capture something you expect to be released, causing really tricky to trace memory leaks.
Is there an option to change the target framework?... I know with some decompilers they default to the lowest level framework (C# 1.0)

Trying to use Activator.CreateInstance(MyType)

I have the following problem:
public class MyType
{
public void method(int a, params object[] p){}
public void MyType()
{
method(5);
}
}
When I use a constructor everything is fine but when I try to use Activator.CreateInstance(MyType);
void Factory()
{
string componentPath = "MyType.dll";
Assembly component = Assembly.LoadFrom(componentPath);
Type myType= component.GetType("MyType");
Activator.CreateInstance(myType);
}
it fails with exception MyType.method(int32) is not found.
Important to notice that before I added params object[] p to method everything worked fine.
Thank You.
If you use methods with optional parameters or methods with params to pass a variable number of parameters, what you're doing is telling the compiler that when you CALL that method, will it please insert the necessary parameters for you? Optional parameters and params arrays are inserted in the calling code, not the called code. (See one of Eric Lipperts blog posts on optional parameters for some elaboration).
You're not using the C# compiler, and the Reflection API does not insert those parameters for you. For example, you can test this not just by reflection, but also by using two assemblies: Assembly A declares method(int X); it is compiled and the dll is referenced by assembly B. This assembly B contains a call to method(42). This works fine! Now, if you recompile assembly A and change the signature to method(int X, object bla=null) or method(int X, params object[] blas), then assembly B stops working - it contains an invalid call. Even so, the source code to assembly B is still OK - you just need to recompile.
Reflection simply happens not to do any of the optional parameter magic for you. It could, certainly - but it doesn't. While reflection doesn't support this, the DLR does, which brings me to the following...
Workaround: Try using the C# keyword dynamic, if possible (which for constructors, it isn't AFAIK) - that attempts to emulate C# calling conventions more closely and supports stuff like optional and named parameters. You may need to change the way your API is set up, however to use methods rather than constructors. It's hard to give more precise advice without seeing actual code.
Alternatively: You may be trying to load plugins, by the looks of it. .NET has some premade infrastructure to help you with this: Add-ins and Extensibility, which may make your task easier.
(Note: your example code is incomplete - I'm making a slight guess that method is in reality a constructor, but you should really post the actual code or at least a snippet that actually fails).
This won't work because you have to pass at least 2 parameters in your call to method(). The params modifier doesn't mean "optional".

Categories

Resources