c# - Changing Delegate signature in library to omit an argument does not break applications using it -
consider following code in class library:
public class service { public delegate string formatter(string s1, string s2); public void print(formatter f) { console.writeline(f("a", "b")); } }
and here's console application uses it:
static void main(string[] args) { s = new service(); s.print(concat); } static string concat(string s1, string s2) { return string.format("{0}-{1}", s1, s2); }
so far prints "a-b", 1 expect.
now, change class library follows:
public class service { public delegate string formatter(string s1); public void print(formatter f) { console.writeline(f("a")); } }
i.e. removed 1 parameter delegate. compile class library , overwrite dll sitting next console app (console app not recompiled). i'd expect breaking change in library , if execute app, finds mismatch, resulting in runtime exception.
in contrast, when run app there's no exception @ all, , stunning output "-a". when debug, can see concat method (with 2 parameters) called, call stack level below shows print calling f("a") (one parameter), no error indication anywhere. interestingly, in concat s1 null, s2 "a".
i played around different changes signature (adding parameters, changing parameter type) same result. when changed type of s2 string int got exception, not when concat method called, when tried call string.format.
i tried .net target framework 4.5.1 , 3.5, x86 , x64.
can answer whether expected behaviour or bug? seems pretty dangerous me.
here's simpler repro - basically, i'm using "under hood" constructor on delegate type (the 1 il uses) pass method target wrong signature, and... works fine (by mean doesn't throw exception - behaves code):
using system; static class p { static void main() { // resolve (object, intptr) ctor var ctor = typeof(func<string, string>).getconstructors()[0]; // resolve target method var mhandle = typeof(p).getmethod(nameof(concat)) .methodhandle.getfunctionpointer(); object target = null; // because: static // create delegate instance var del = (func<string, string>)ctor.invoke(new object[] { target, mhandle }); var result = del("abc"); console.writeline(result); // "-abc" } public static string concat(string s1, string s2) { return string.format("{0}-{1}", s1, s2); } }
this not explanation. might helpful if want ask more clr-expert! have expected delegate constructor have complained loudly target being incorrect.
at guess (pure speculation), case of: if you're passing intptr
(native int), you're entirely on own - code fastest thing possible. seem nasty trap unwary, though!
as why s2
has value , s1
empty: guess because stack builds down (not up), hence in 2 parameter method, arg1
parameter immediately adjacent previous position on stack. when pass single value instead of two, put 1 value underneath, s2
has value, , s1
undefined (could garbage previous code).
Comments
Post a Comment