I decompiled a C# assembly using ILSpy. Opened it as a project in VC.
A small portion of the code throws errors that I don't know how to fix. Here's the code:
public static class CoroutineUtils
{
[DebuggerHidden]
public static IEnumerator WaitForRealSeconds(float time)
{
CoroutineUtils.<WaitForRealSeconds>c__Iterator2F <WaitForRealSeconds>c__Iterator2F = new CoroutineUtils.<WaitForRealSeconds>c__Iterator2F();
<WaitForRealSeconds>c__Iterator2F.time = time;
<WaitForRealSeconds>c__Iterator2F.<$>time = time;
return <WaitForRealSeconds>c__Iterator2F;
}
}
And here's the error: Unexpected character '$' (at line 8 in this case).
And if I open the .cs file in which the error appears, the compiler starts throwing a dozen more errors like Identifier expected at line 6 (right after "CoroutineUtils.")
Don't know what to do.
You can't just copy/paste decompiled code and be sure it will work. Compiler can use identifiers that are not valid in C# code but are valid in IL. It happens mostly for compiler generated code - automatic properties, anonymous types, iterators and async/await converted to state machines, etc. That's the case here.
It's really hard to say what the code is suppose to do, so it's really hard to say how to fix it.
Related
I made a C# compilation program with Roslyn. However, it takes about 1 second to compile the entire project.
I'm trying to optimize the time and here's my try:
// I'm currently recycling the compilation object.
if (compiler == null)
compiler = CreateCompiler();
/* ... */
for (var tree in syntaxTrees) {
SyntaxTree oldTree;
if (PreviouslyAddedAndHasChanges(tree)) {
compiler = compiler.ReplaceSyntaxTree(oldTree, tree);
}
else if (NewlyAdded(tree)) {
compiler = compiler.AddSyntaxTree(tree);
}
}
compiler.Emit(...);
But it doesn't work. Output assembly won't be changed.
I also take a look EmitDifference method, but it does not work with .dll output.
Here's my question:
Is it safe to re-use CSharpCompilation object just like my first line of the code?
Does Roslyn caching previous compilation data and should I invalidate it?
Self answer:
I fixed it now. The problem was not related with Roslyn.
The two assemblies have exactly the same name, so Assembly.Load() does not work at all.
Here's my solution to fix it.
compiler = compiler.WithAssemblyName("some_name" + (new System.Random()).Next(10000000));
And, the answers are:
Yes
I'm not sure, but it seems that they re-compile it if there is any changes.
I have a line of script which gets a class refrence from third party script(NGUI package):
UIEventListener.Get(this.gameObject).onClick += expiryDateSettingsUIController.ActiveDeactiveUICaller;
The UIEventListener class belongs to NGUI package. My problem is that I don't want to show any error if that particular class doesn't exists. How can I do that?. If the class is not available then it is throwing compiler error and won't allowing me to build the exe.
You can use GetType() to reflect the class you are looking for:
Type mType = Type.GetType("UIEventListener");
if(mType != null)
{
UIEventListener.Get(this.gameObject).onClick += expiryDateSettingsUIController.ActiveDeactiveUICaller;
}
C# is a compiled language, so before you can run any code, it needs to be error-free. An absent class is a very serious compilation error. There is no way (in Unity or otherwise) that you can achieve the behavior you're asking for.
However, you can use conditional compilation. Conditional compilation, as the name suggests, is when you strip pieces of code out during compilation, so those pieces are never compiled and no compilation errors will be encountered for those. Wrap your code in #if/#endif like below:
#if WITH_NGUI
UIEventListener.Get(this.gameObject).onClick += expiryDateSettingsUIController.ActiveDeactiveUICaller;
#endif
Then, to let this code compile, you will need to define the WITH_NGUI conditional compilation symbol. In Unity, you can go to Player Settings, and there will be a box named Scripting Define Symbols where you can type in as many symbols as you like. Type WITH_NGUI into that box, and the code will compile. When you don't have NGUI available, simply remove the symbol and the code won't compile any more.
This isn't a nice thing to do though. You've been warned.
EDIT: You could also use reflection to access classes which may be absent from your build, but that's way more trouble than it's worth.
I have a huge code base and I recently made a change where I changed the type of a parameter from String to a custom class. On the next compile I got all the areas where the impact was, but areas where the input type was of type Object failed. for e.g.
String str = "32"
int i = Convert.ToInt32(str)
Now I have changed String to a new custom type lets say MyCustomClass I would now want following code to fail on next compile
MyCustomClass str = new MyCustomClass("32")
int i = Convert.ToInt32(str)
but it won't as Convert.ToInt32 also accepts type Object. Is there some way I can make a change in MyCustomClass that it's not considered Object anymore.
Please note: Convert.ToInt32 is only used for sample I have many more such functions, so please focus your suggestion/answer to question asked.
Override ToString() and IConvertible
You said in the comments that your intentions are to find places where your object, which had previously been treated as a string, and are now being treated as an object.
In these situations typically, the third-party code would call .ToString() on your object to get something which it can use.
So, Convert.ToInt32(str) is equivalent to Convert.ToInt32(str.ToString()).
If you implement ToString() and IConvertible to return whatever your old version of str looked like then it should continue to work in the same way as the old version.
Probably.
Sorry I know that is not the 100% perfect compile time answer you were looking for, but I think you also know very well that your MyCustomClass will always be considered object.
Possible compile time answer:
Write a tool which uses reflection to iterate over every class/struct/interface in every system/third-party DLL.
Output a load of CS files which contain all these same classes, but just throw NotImplementedException.
(T4 could help you do this)
Compile these classes into dummy.dll
Your .csproj now references only this one dummy.dll, instead of the real dlls.
Your project should compile fine against the dummy dll.
Look at your dummy.cs files and delete any use of object.
Re-compile... and suddenly you get a load of compile time errors showing you anywhere you are using an object.
Impliment an implicit cast from MyCustomClass to String.
public static implicit operator string(MyCustomClass str)
{
return "Legacy respresentation of str";
}
This allows the complier the choice of choosing ToInt32(Object) or ToInt32(String), and I bet it favours the later.
This way all your existing function calls will remain the same so you wont have to be concerned about third party implentation details.
(Sorry, I am not at a computer right now so I can`t test that my assumtion is correct. If you do test this, be sure to consider extension methods, as they can affect the conpilers desision making in unexpected ways)
① In following C# code, CS1729 occurs but I understand that CS0122 would be more appropriate.
namespace A
{
class Program
{
static void Main()
{
Test test = new Test(1);
}
}
class Test
{
Test(int i) { }
}
}
CS1729: 'A.Test' does not contain a constructor that takes 1 arguments
CS0122: 'A.Test.Test(int) is inaccessible due to its protection level'
② In following C# code, CS0122 occurs but I understand that CS1729 would be more appropriate
namespace A
{
class Program
{
static void Main()
{
Test test = new Test();
}
}
class Test
{
Test(int i) { }
}
}
CS0122: 'A.Test.Test(int) is inaccessible due to its protection level'
CS1729: 'A.Test' does not contain a constructor that takes 0 arguments
Question: Is there any reason why CS0122 and CS1729 are swapped in ① and ② or is this C# compiler bug ?
P.S.: Errors in ① and ② can be reproduced with Microsoft Visual C# 2010 Compiler version 4.030319.1.
Full disclosure: I work on the C# team at Microsoft.
Diagnostic reporting from a compiler is a tricky business! We spend a lot of time trying to ensure that the "best" diagnostic is reported for a particular error condition. However, this sometimes requires taking heuristics into account, and we don't always get that right. In this case, as #Henrik Holterman points out, both errors are reasonable (at least for the second case).
The first example is clearly a bug, though it's of low severity. After all, it's still an error with a somewhat "correct" (I'm being a bit gracious here) diagnostic. In the second example, both errors are correct, but the compiler failed to pick the "best", and hopefully, the most helpful diagnostic.
With the Roslyn C# compiler, we've had an opportunity to take a fresh look at our diagnostic reporting and make better choices. For these particular examples, the Roslyn compilers do in fact produce the errors that you were expecting. In the first example, CS0122 is reported, and in the second case, CS1729 is reported. So, you can rest assured that this is already fixed in a future release.
I am working on a game which uses C# and C++. Classes for models are written in C# and levels structure is stored in XML files. When I want read it in C++ and want to build project I have this strange error and I don't where to find some bugs.
Error 1 error C3699: '*' : cannot use this indirection on type 'Cadet::XMLReader::Models::Obstacle' C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\xmemory0 527 1 Cadet.Game
These kind of errors are in xmemory0 and list files? what they are? and it happend only for Obstacle class, the rest are fine.
Here it is part of the code
void SetupObstacles(std::list<Cadet::Game::Entities::Obstacle> &obstacles)
{
int size = CurrentLevel->Obstacles->Length;
Cadet::XMLReader::Models::Obstacle^ currentObstacle;
}
It looks like Cadet::Game::Entities::Obstacle is a managed class (since you've declared currentObstacle as a reference with ^). If that's the case, you can't directly store managed objects in STL containers like std::list<>.
It's hard to say what to do next w/o more context, but one possible fix would be to change your SetupObstacles method:
void SetupObstacles(System::Collections::Generic::List<Cadet::Game::Entities::Obstacle>^ obstacles)
{ ... }
Do you have a pointer to an Obstacle somewhere?
The help on this error suggests that some types (such as trivial properties) cannot have a reference type--you can't have a pointer to it. Try using ^ instead.