Trying to understand method signature changes spanning assemblies - c#

We ran into a strange problem with a promotion and I'm hoping I'll be able to explain it with code. I want to understand why it behaves in the manner it is.
Assembly 1
public static class Foo
{
public static string DoStuff()
{
// Do something
return "some string";
}
}
Assembly 2:
public class Bar
{
public void SomeMethod()
{
// I realize the below is not what should be done for catching exceptions, as the exception is never thrown due to the return, and seems unnecessary either way...
// this is inherited code and has not been modified to correct.
try
{
var someValue = Foo.DoStuff();
}
catch (Exception)
{
return;
throw;
}
}
}
Requirements changed so that DoStuff would need to take in a parameter, the value of which would change the behavior slightly. Note that only assembly 1.Foo is changing.
New Foo
public static class Foo
{
public static string DoStuff(bool someBool = false)
{
// Do something
return "some string";
}
}
This recompiled fine, and Assembly 2 was able to successfully utilize the changed method signature without complaint. My check in was performed, and project dlls that had changes were promoted (note this was only Assembly 1 dll).
After promotion we found out that Assembly 2 was failing on the Foo.DoStuff() call - unfortunately I cannot provide an exception as the above code was swallowing it.
Even though no actual code changed in Assembly 2, it did seem to have an impact on the dll on recompile, even though the method signature - at least in my mind - is the same due to providing a default value for the new parameter.
I used dotnet peek in order to peek at the "old dll" and the "new dlls" (even though no code change to that assembly and did note a difference in the two DLLs in that in the old DLL, the default value parameter was not supplied, but in the recompiled, the default value parameter was supplied.
I guess my question(s) boil down to this:
Why does the compiled code on assembly 2 change based on a method signature that (at least I think) should be transparent?
Would the method of avoiding a situation like this just be "deploy everything"? Rather than trying to deploy based on code changes? Note that DLLs are not checked in, so there was no actual "code change" to assembly 2, though the DLL was different based on the changes to Assembly 1.

Why does the compiled code on assembly 2 change based on a method signature that (at least I think) should be transparent?
No, it shouldn't. When you don't specify an argument to correspond with an optional parameter, the default value is provided at the call site - i.e. assembly 2 in your case. That's how optional parameters work in C#. It's a pain, but that's life.
Would the method of avoiding a situation like this just be "deploy everything"?
Yes. Basically, the meaning of the source in assembly 2 has changed even though the code itself hasn't - so the compiled code has changed, and it needs to be redeployed.
You can see subtle breaking changes like this in other cases, too - where the same old "client" code compiles against new "receiving" code, but has a different meaning. Two examples:
Suppose you change a method signature from Foo(long x) to Foo(int x) but at the call site, you only call it as Foo(5)... that compiles in both cases, but to different code.
Suppose you have change a method signature from Foo(int x, int y) to Foo(int y, int x) and you have a call of Foo(x: 5, y: 2)... again, the meaning of the code changes from Foo(5, 2) to Foo(2, 5), if you see what I mean. Recompiling the unchanged source code against the new "receiving" code will change the behaviour.
Suppose you have a constant in assembly 1, e.g. public const string Foo = "Foo";. If you change that to public const string Foo = "Bar", but don't recompile assemblies using the constant, those assemblies will still use a value of "Foo". (This goes for default values of optional parameters, too.)

Related

How can I get the number of enums as a constant?

From Total number of items defined in an enum, I see I can get the number of enums by using:
Enum.GetNames(typeof(Item.Type)).Length;
Works great!
But, I need this number as a constant number, so that I can use it in Unity's [Range(int, int)] feature.
private const int constEnumCount = Enum.GetNames(typeof(Item.Type)).Length;
The above does not work, because the enum count is not a constant number, so I cannot assign it to a constant variable.
How can I get the number of enums as a constant?
It's not possible to get the number of enums as a const. Enum.GetNames uses reflection to get these things, and that's inherently a runtime operation. Therefore, it can't be known at compile time, which is a requirement for being const.
Unfortunately, this is not possible to do in any meaningful way due to technical limitations:
[Range(int,int)] is an attribute, and all information provided to an attribute has to be a const
The only truly bulletproof way to get the number of values in an enum is to use Enum.GetValues(typeof(MyEnum)).Length or the Enum.GetNames(typeof(MyEnum)).Length, both of which are run time reflection.
However, there are hacks that can sort of work. For example, the value of an enum can be cast to an integer. As long as nobody is explicitly defining values for your enums you can use the last element in your enum kind of like this:
[Range(0,(int)MyEnum.LastValue)]
public void BlahBlahBlah() {}
However, know that as soon as someone adds a new entry to the enum after the one you are using or reorders the elements in the enum, your code will break unpredictably and not behave like you want.
It's sad, but the C# compiler is not smart enough to do simple math in the compiler like Java, C, and C++ compilers. So even the example I gave would only really work if the LastValue wasn't ever used for anything except to mark the last element in the enum. It lowers the complexity of the C# compiler, which also greatly improves the compilation speed for your application. As such, there are some trade-offs that the C# and CLR team took to improve your experience in other places.
Assuming that you need a const because you are trying to specify the values for an Attribute (this one?) then you are out of luck.
Your options are:
Hardcode the count in the attribute declaration or as a const itself and be careful to keep the count in sync with the enum definition
Use PostSharp or some other aspect oriented framework to insert the attribute for you. Have never done this myself but it looks possible (How to inject an attribute using a PostSharp attribute?)
You could probably also finagle some way to so this with T4 templates but that would get excessively kludgy.
Were it me, unless you are talking but hundreds of different set of enumerations, I'd hardcode the length and maybe add an Assert where I needed it.
// WARNING - if you add members update impacted Range attributes
public enum MyEnum
{
foo,
bar,
baz
}
[Range(0, 3)]
class Test
{
public Test()
{
int enumCount = Enum.GetNames(typeof(MyEnum)).Length;
int rangeMax = GetType().GetCustomAttributes(typeof(Range), false).OfType<Range>().First().Max;
Debug.Assert(enumCount == rangeMax);
}
static void Main(string[] args)
{
var test = new Test();
}
}
Super late to the game here but I've always used a nifty hack when using enum defaults (i.e. no custom values).
public enum MyEnum {
foo,
bar,
baz,
count
}
Since enums default to starting with 0, ensuring the last entry is named simply 'count' ensures that the value of count is in fact the number of values in the enum itself.
private const int constEnumCount = (int)MyEnum.count;
Cheers!
You cannot assign/create a const at runtime. That is what you are trying to do. A const has to be fully evaluated at compile time, not runtime.
I am not sure about Unity (and why it requires a const), but I would look for using readonly
private readonly int constEnumCount = Enum.GetNames(typeof(Item.Type)).Length;
In C#, a const is defined as constant, i.e. the value (not the calculation which produced it!) is directly embedded in the compiled assembly.
To stop you from doing something problematic, the compiler does not allow you to assign the result of a calculation to a const. To understand why, imagine it were possible:
A.dll
public enum Foo { A, B }
B.dll
public const NumberOfFoos = Enum.GetNames(typeof(Foo)).Length;
// Compiles to:
B.dll
public const NumberOfFoos = 2;
Now you change A.dll:
A.dll
public enum Foo { A, B, C, D }
B.dll
public const NumberOfFoos = 2; // Oh no!
So you would need to recompile B.dll to get the correct value.
It gets worse: Imagine you had an assembly C.dll, which uses B.NumberOfFoos. The value 2 would be directly embedded in C.dll, so it also would need to be recompiled, after B.dll, in order to get the correct value. Therefore (pit of success), C# does not allow you to do that assignment to a const.

C# Getting Error: Type "Indirect.Class" Not Defined In an Assembly Not Referenced . Must Add a reference to assembly "Indirect"

I realize this question has been brought up, but mine is being caused for different reasons than i've seen on the similar questions, so here is my setup.
I have 2 c# projects, A and B, and project B reference a third party library, Indirect. A will call a static method defined in B, and when I build I get the following error.
Error 2 The type 'Indirect.Class' is defined in an assembly that is not referenced. You must add a reference to assembly 'Indirect, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Here is the code:
//Project A
class A
{
static void Main(string[] args)
{
B.TestMethod("fileName", "param2");
}
}
//Project B
public class B
{
public static string TestMethod(string fileName, string param2)
{
return "";
}
public static bool TestMethod(Indirect.Class doc, string anotherParam)
{
return false;
}
}
So even though in A I am calling the B method that does not depend on the Indirect assembly, I still see the error. Can someone explain to me why the compiler is having trouble determining this at compile time? I could simply rename the second B.TestMethod and the error would go away, or I could add a third parameter to it, but all of this seems unnecessary.
The compiler has to pick the best matching overload, and in doing so, it evaluates the number of conversions necessary for each candidate and then ranks them.
You and I know that because there is an exact match, it doesn't matter how bad the others are, because they can't ever win. But that's not how the language rules are written, nor how the compiler works.
The compiler doesn't know you aren't calling the second overload until it ranks all the overloads, including the one you aren't calling. To do that, it checks whether the actual parameter (of type string) can be converted to the type of the formal parameter (Indirect.Class), and it needs to look inside the assembly for Indirect.Class because that's where such a conversion would be defined.

Enum.GetName() As Constant property

I have been working in C# for about 8 months so forgive me if this is dumb...
I have an enum that I will need the string value several times in a class. So I want to use Enum.GetName() to set it to a string variable which is no problem. I just do it like so...
private string MyEnumString = Enum.GetName(typeof(MyEnum), MyEnum.Name);
And it works just fine.
But I tried to protect it a little better because this particular Enum is more important that all the others and it would not be good if I accidentally changed the string value somehow so I tried to make it const like this.
private const string MyEnumString = Enum.GetName(typeof(MyEnum), MyEnum.Name);
To my eyes this seems fine as it should all be known at compile time.
But Visual Studio 2013 Throws an error saying the "Cannot resolve symbol GetName". I know it works when it is not marked "const".
So this leads me with two questions about this?
Why does it loose reference to the GetName enum? (After a bit of research I suspect it is something to do with GetName being a method and not a property of the Enum class but the error message just does not make sense to me)
And Finally is there a way to read the Name of MyEnum.Name to a const string other than what I am doing?
Just make it readonly:
private readonly string MyEnumString = Enum.GetName(typeof(MyEnum), MyEnum.Name);
Then it can't be changed afterwards.
You can't assign the result of calling a method to a constant; C# just doesn't allow it - the compiler would have to be calling that method at compile time, possibly before it was even compiled (and not only would it have to generate the IL, it would have to use the JIT compiler to compile that IL).
Enum.GetName(typeof(MyEnum), MyEnum.Name); is calling a method, so you can't assign the result to a constant.
[EDIT] As Jon Skeet says in a comment above, you can use nameof if using C#6 or later (i.e. VS2015 or later):
private const string MyEnumString = nameof(MyEnum.Name);
nameof works because here you are not calling an arbitrary method, but you are instead using a compiler feature to access the name of a type.
You cannot use result of the method as constant, because method evaluation can occur only at runtime. The value of the constant must be known at compile time. In order for compiler to be able to evaluate that constant, it would need to know the semantics of Enum.GetName and execute it at compile time, which is not possible
You can mark it as static readonly instead. That way it will be set once per type where it is declared and it cannot be changed anymore at runtime.
It may not even be known at run-time:
From MSDN:
If multiple enumeration members have the same underlying value, the GetName method guarantees that it will return the name of one of those enumeration members. However, it does not guarantee that it will always return the name of the same enumeration member.
(emphasis added)
void Main()
{
Console.WriteLine (Enum.GetName(typeof(Test),Test.One));
}
public enum Test
{
Zero,
One,
Two,
Uno = 1,
Dos = 2,
}
I consistently get the output Uno for the program above.
The reason is it not known is because enums are compiled to the underlying value. The call above is essentially compiled to Enum.GetName(typeof(Test), 1). GetName looks for a member with that value to find the name. How it does that is apparently an implementation detail that may not product consistent results.
What you can use for a constant in C#6 and later is nameof:
private const string MyEnumString = nameof(MyEnum.Name);

CA1026 (all parameters should have default values) and extension methods

Premise
When using code analysis (or fxCop) with C# optional parameters you can get a warning of CA1026. The short reason1 for this is not suppling all parameters with a default value.
The declaration below rightly generates this warning
public Color GetColor(bool red, bool blue = true, bool green = true)
However there is a situation where you could not supply all parameters with a default, and that is extension methods. So the declaration below generates the warning because of the first parameter:
public static bool ValidateRules(this string s, Rules rules = Rules.Default)
The compiler will not let you specify a default value on the this parameter so the only two solutions is to:
Ignore the warning, which I do not like doing because it leads to bad practices.
Not use extension methods, which I do not like doing because I find extension methods make the code more readible.
Questions
Are the above two options the only
way to solve this?
Is fxCop/Code
Analysis incorrect in it's check?
The long reason
It's not warning you for not having defaults for all parameters - it's warning you for using optional parameters at all.
Personally I would disable this particular warning. When used with care, I think optional parameters are fine. You should think carefully about them particularly in terms of versioning of the default parameter value and in terms of languages which don't support them (including C# before v4) but in many environments the downsides really aren't an issue - and you can end up with much simpler code than by specifying overloads all over the place.
An argument that I am missing in Jon Skeet's answer is also about maintainability: Default values are always filled in with it's value in the IL (intermediate language). This is an issue if you're using external libraries.
Here are steps to reproduce a simple example:
Create a console app
Add a ClassLibrary project to it
Add the following code:
Program.cs
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var obj = new Class1();
Console.WriteLine(obj.Foo());
Console.ReadKey();
}
}
}
and in your Class1.cs
namespace ClassLibrary1
{
public class Class1
{
public string Foo(string str = "http")
{
return str;
}
}
}
If you run it you will see 'http', as expected.
Now change "http" to "https"
Compile only the library (maybe even unload the console project)
Copy the dll from the library's bin folder to the console app's bin folder by hand
Run the console app from the command line, not from within VS!
You will still see http! With ILSpy you can see that http is hardcoded in the console app.
In this case this could lead to a security issue if the developer thinks he is safe by replacing the "http" to "https" in the default value.
So if external libraries are updated always compile your code again. Or just don't use default values.
Just create a separate method:
public string Foo()
{
return Foo("https");
}
public string Foo(string str)
{
return str;
}
You can suppress the warning on a case-by-case basis.

Initialize library on Assembly load

I have a .net library dll that acts like a functional library. There are a bunch of static types along with static methods.
There is some initialization code that I need to run to set up the library ready for use.
When the assembly gets loaded is there a way to ensure that a particular method is run? Something like AppDomain.AssemblyLoad but called automatically from the assembly itself. I was thinking that maybe there is something like an AssemblyAttribute that could be used?
At the moment I have this initialization code in a static constructor but as this is a library with many entry points there is no guarantee that this particular type will be used.
Thanks!
Yes there is - sort of.
Use the excellent little utility by by Einar Egilsson, InjectModuleInitializer.
Run this executable as a post build step to create a small .cctor function (the module initializer function) that calls a static void function of yours that takes no parameters. It would be nice if compiler gave us the ability to create .cctor(), luckily we rarely need this capability.
This is not a complete DllMain replacement, however. The CLR only calls this .cctor function prior to any methods called in your assembly, not upon assembly load. So, if you need something to happen upon assembly load, you need to have the loading code call a method directly or use the hack I detailed https://stackoverflow.com/a/9745422/240845
The following solution is possible only when you have control over the main executing assembly, i.e. it's not suitable for standalone libraries meant for distribution
I've had a similar problem and solved it by creating an assembly-targeted attribute 'InitializeOnLoad' with a Type parameter. Then, in the main executable, I've added a trivial AppDomain.AssemblyLoaded handler, which scans the newly loaded assembly for the aforementioned attribute and calls System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor() on them.
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class InitializeOnLoadAttribute : Attribute
{
Type type;
public InitializeOnLoadAttribute(Type type) { this.type = type; }
public Type Type { get { return type; } }
}
// somewhere very early in main exe initialization
AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(AssemblyInitializer);
static void AssemblyInitializer(object sender, AssemblyLoadEventArgs args)
{
// force static constructors in types specified by InitializeOnLoad
foreach (InitializeOnLoadAttribute attr in args.LoadedAssembly.GetCustomAttributes(typeof(InitializeOnLoadAttribute), false))
System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(attr.Type.TypeHandle);
}
additionaly, if you are afraid that assemblies might have been loade before you hook the AssemblyLoad event, you can simply run through AppDomain.GetAssemblies() and call the 'initializer' for them.
Why do you need all the data to be loaded before any of it is used, rather than just when the first type which needs it is used?
I don't believe there's any way of forcing a method to be run on assembly load, from within the assembly. You could put a static constructor in every type, but frankly I think it just makes more sense to have a single type representing that data and providing access to it - and put a static constructor on that type alone. (If you've got separate bits of data which can be used independently, perhaps create separate types for them.)
It's possible - just add a static constructor to the <Module> class. I don't know how to accomplish this without IL modification though.

Categories