DiagnosticAnalyzer for type usage - c#

I would like to write DiagnosticAnalyzer, which analyzes if a class (ie. BotAggregate) declared in a MyProject.Domain assembly has it's own DbSet<BotAggregate> declared in MyProjectDbContext class which is placed in MyProject.EFCore assembly.
When I do place the analyzer in MyProject.EFCore project, it does not even see the BotAggregate class declaration (because it scans the assembly where analyzer is referenced). And if I place the analyzer in MyProject.Domain it could not access the MyProjectDbContext to check if there is a required property.
Is it even possible to "cross check" the declaration and usage by Roslyn analyzer?

Related

What C# compiles when using namespaces

We have two applications that each compile their own code with some of the code being shared (mainly data related). It felt natural to split this design up into three namespaces and try to enforce that namespace Foo never imports namespace Bar but either can import namespace Shared.
I hope Venn diagrams are appreciated as a visualization:
However one of the classes between Foo and Bar slipped through the cracks and someone referenced a class from Bar inside Foo despite the enforcement.
And that got me wondering if how the C# compiler actually deals with this? The way I see it, one of two things could happen.
The entire namespace gets compiled into Foo. Leaving the diagram to look like this:
Or the compiler is smart enough just to extract the necesarry class. Making the diagram look like this:
I can't seem to find any documentation on how usings and namespaces compile. It seems like namespaces are just to organise code for developers, not compilers. Yet they provide scope... So I guess #2 applies then? How to even test this?
There is no correspondence between namespaces and assemblies: one assembly may contain many namespaces, and one namespace may span many assemblies.
The compiled IL code in an assembly refers to types by their fully qualified names: Foo.SomeClass instead of SomeClass, Bar.OtherClass instead of OtherClass, and so on. The compiler's job is to figure out which fully qualified type name you really mean when you write the shortened form SomeClass -- because you could have defined a class called SomeClass in the namespaces Foo, Bar, or even System!
When you write:
namespace Foo
{
public class SomeClass
{
}
}
You are defining a type with a fully qualified name Foo.SomeClass.
When you write:
using Foo;
...
SomeClass instance = new SomeClass();
The compiler treats this the same as:
Foo.SomeClass instance = new Foo.SomeClass();
Namespaces are just a construct of convenience for organizing these fully qualified names. When you say using Foo;, you are just telling the compiler to search for fully qualified names that start with Foo. whenever you type SomeClass. There is nothing being "imported" when you write using Foo;, it just provides a convenient alternative to writing Foo.SomeClass everywhere; nor does any "code" (in the sense of IL instructions being emitted) get generated by your usings or your namespaces. All it does is tell the compiler to put Foo.SomeClass into the IL whenever you write SomeClass.
The above is a simplification of a more nuanced set of rules defined in the spec for resolving short-form type names; you can read this for more details: here and here
The level at which you'd want to enforce the dependencies in your diagrams would be at the assembly reference level: if the Foo project never references the Bar assembly or vice versa, the code would not even compile if you tried to reference a type in one assembly from the other. The namespaces don't really have much to do with that at all, because again, nothing stops you from defining types in the Foo namespace but in the Bar assembly.

Is it possible to apply an attribute to a class and assembly at the same time?

I need to find all types with a [MyAttribute] in a given assembly as fast as possible. Obviously the fallback is to just use GetTypes and check IsDefined on each of them, but I'm looking at ways to speed it up because this code will be run for multiple assemblies every time they are loaded (in Unity this means every time the user modifies a script).
I've come up with two potential solutions:
Make an assembly attribute containing a method which uses GetTypes and IsDefined to build a string declaring that attribute like: [assembly: AssemblyContains(new Type[] { typeof(TypeA), typeof(TypeB), ..., and then log that string so it can be copied into one of the source files of the assembly.
Make an assembly attribute that needs to be declared alongside every instance of MyAttribute like: [assembly: AssemblyContains(typeof(MyClass))] [MyAttribute] public class MyClass { ... }.
I expect #1 would be a tiny bit more efficient, but it would be more effort to use and you could easily forget to update it when adding new classes, so I'm leaning towards #2.
So my question: is #2 a good solution and/or can you suggest a better approach?

Why do we use internal classes for constants in C#?

I have seen it in many projects. Why do developers use internal classes to store the constant variables in C#?
For instance:
internal static class Constants
{
public const double Pi = 3.14159;
public const int SpeedOfLight = 300000; // km per sec.
}
Simply, the designer decided that this class need to be used within the same assembly. And it is not to be exposed to or accessed from any project referencing the assembly.
When you download a Nuget package, you can't access classes that are internal. The developers decided that you don't need to access these. So these values are "private" for this package.
More on access modifiers:
public :Access is not restricted.
protected :Access is limited to the containing class or types derived from the containing class.
internal: Access is limited to the current assembly.
protected internal : Access is limited to the current assembly or types derived from the containing class.
private : Access is limited to the containing type.
Because when constants are publicly exposed (instead of internal), the danger exists that when an outside assembly references them, it may become "out of date" when the assembly that declares them is updated with new constant values, but the referencing assembly is not re-compiled.
Say, for example, the referenced assembly containing the "updated" constant values forms part of a "plugin" architecture, where a new version of the plugin could simply be "dropped" into the referencing application's folder without recompiling and redeploying the application. Even if the application that referenced a constant from the "plugin" assembly that originally declared it, the application will not use the new updated values, and will continue using the old ones instead.
This happens because when the compiler sees a constant, it "inlines" its value into the expressions or statements that contain ("reference") it, wherever that may be.
To solve this problem, and you really want to expose a "constant" to the outside world, rather declare the symbol as public static readonly. That way, if the value is ever updated, any outside assemblies that reference it will automatically use the update value(s), even without needing a recompile.
But if you really want to use const instead, make sure it is really a constant that will never change, i.e. laws of nature, physical constants, like Pi.
While most answers describe the meaning of internal they are missing a part of the question I think: Why put constants in a inner class?
This is mostly because it can they desire to identify constants through something feeling like a namespace:
double myValue = Constants.Pi * 10;
Or
double myValue = OtherClass.Constants.Pi * 10;
Without the inner class they would look more like members/properties. At least when they do not use UPPER_CASING for the naming.
To make the answer complete: The internal is used to protect the constant against using them outside of the assembly.

Same class name and assembly name in .net

I have a class MyComponent in my project which is being contained in another assembly(MyAssembly). This class is being used at many places in my project as parameter of various functions or variable type e.g.
private void MyMethod(MyComponent com)
{
//Method Implementation
}
MyComponent varCom;
But I want to include an assembly which has a same name as my class i.e. MyComponent. Now whenever I include this assembly, wherever MyComponent is being used as it start showing error
"'MyComponent' is a 'namespace' but is used like a 'type'".
One way to resolve it would be to give the full path for the reference path at all the places for MyComponent variable as shown below.
MyAssembly1.MyComponent varCom;
but it would require lot of code changes which I dont want to do. Is there any other way out for this problem
P.S The error is "'MyComponent' is a 'namespace' but is used like a 'type'"
I think the simplest solution in your context would be renaming your MyComponent class by refactoring before you reference the other assembly. Right click the class name, Refactor, Rename. This should automatically update the class name everywhere it's used.
Or you could do the same for the MyComponent name space.

Strange behaviour of WPF project (class naming)

I got a strange error when tried to build my project ExpertSystem in solution ExpertSystem:
Error 1 The type name 'App' does not
exist in the type
'ExpertSystem.ExpertSystem' D:\Users\Kirill\Documents\Visual
Studio
2010\Projects\ExpertSystem\ExpertSystem\obj\x86\Debug\App.g.cs 60 26 ExpertSystem
I didn't even knew that VS creates this file while building. So, I started search the problem in my last edits in code and found that problem is in my last class:
namespace ExpertSystem
{
public class ExpertSystem
{
//...
}
}
When name of class is changed to something different from ExpertSystem, project compiles without errors.
Can anyone explain, can I actually have classes in C# with the same name as namespace/project/solution? Or is this a some kind of VS/WPF bug?
Thanks.
VS generates partial class for each XAML file (not during build, but during design), in order (for instance) to declare and fill the named components as class fields.
If you want to easily read the content of the designer generated App.g.css file (associated with the App.xaml and App.xaml.cs file), go to the App.xaml.cs file and perform a "Go to Definition" on the InitializeComponent() function call in the class constructor. I don't know what lurks in your, but I would expect that the designer generated something like this (maybe not this, but the issue will be the same):
var foo = (SystemExpert.App)(Application.Current)
Which should be understood as:
var foo = (global::SystemExpert.App)(Application.Current)
Now, if you create a SystemExpert class in your SystemExpert assembly namespace, and as the App class is declared in the SystemExpert namespace too, the compiler will understand that:
var foo = (global::SystemExpert.SystemExpert.App)(Application.Current)
^^^^^^^^^^^^^^^^
the current namespace
Naming a class exactly the same way as a namespace is bad practice: it can confuse the compiler.
Can anyone explain, can I actually have classes in C# with the same name as namespace/project/solution?
Yes, you can. It's part of the C# language.
Therefore the compiler can't figure out whether the code meant to look for the ExpertSystem.ExpertSystem namespace or the ExpertSystem class in the ExpertSystem namespace. (Well it can, but it got it wrong.)
To complement BoltClock's answer with a solution that will work while keeping the namespace and class names as they are:
The error is reported in a file named App.g.cs, which is generated by the compiler. Thus, fixing the issue in that file will not help, as the file will be overwritten with the error upon the next compilation (or rewritten once you have copied the code to another machine).
However, you can change the App.xaml file, from which App.g.cs is generated. The root element of the file will start with something like
<Application x:Class="ExpertSystem.App"
In there, the namespace ExpertSystem is supposed to be found, but with the class having the same name, the compiler assumes that App is a member or a nested type in your class ExpertSystem.ExpertSystem.
By pondering about this, you will realize that the compiler first tries to evaluate the value of the x:Class attribute relatively to the ExpertSystem namespace for some reason. This behaviour is responsible for your problem, but as we now know the specifics of the behaviour, we can write the code accordingly - with an identifier that is qualified relatively to the namespace ExpertSystem:
<Application x:Class="App"
After this change, it should compile fine, even if both the namespace and the class are named ExpertSystem.

Categories

Resources