I want to mark a method as Obsolete, and also cause the compilation to fail if it is called from anywhere.
I came across a solution here How do I mark a method as Obsolete/Deprecated? .
an answer suggests this syntax, saying that the boolean modifier will achieve my wanted effect(failing compilation)
[Obsolete("Method1 is deprecated, please use Method2 instead.", true)]
However, while this worked on the same project; it didn't when calling the method from another project (i even had visual studio productivity power tools show an eror for it but compilation still succeeded)
is this by design? or is there a workaround?
If you use something marked as obsolete inside a method or class which is also marked as obsolete, the compiler will give you no warning or error.
Consider the following obsolete method in some class:
public class SomeClass
{
[Obsolete("Don't use",true)]
public static void ObsoleteMethod() { }
}
The expected behavior is that it yields a compiler error whenever it is used.
But if you use it in another obsolete method, you not even get a compiler warning.
public class AnotherClass
{
public void Method()
{
SomeClass.ObsoleteMethod(); // Compiler error
}
[Obsolete("Avoid use",false)]
public void AnotherObsoleteMethod()
{
SomeClass.ObsoleteMethod(); // No error and no warning
}
}
This is true also if the whole class is marked as obsolete:
[Obsolete()]
public class ObsoleteClass
{
public void Method()
{
SomeClass.ObsoleteMethod(); // No error
}
}
Related
I've just stumbled across a rather dangerous scenario while migrating an ASP.NET application to the async/await model.
The situation is that I made a method async: async Task DoWhateverAsync(), changed the declaration in the interface to Task DoWhateverAsync() and hoped that the compiler would tell me where the code now is wrong, via that Warning. Well, tough luck. Wherever that object gets injected via the interface, the warning doesn't happen. :-(
This is dangerous. Is there any way to check automatically for non-awaited methods that return tasks? I don't mind a few warnings too many, but I wouldn't want to miss one.
Here's an example:
using System.Threading.Tasks;
namespace AsyncAwaitGames
{
// In my real case, that method just returns Task.
public interface ICallee { Task<int> DoSomethingAsync(); }
public class Callee: ICallee
{
public async Task<int> DoSomethingAsync() => await Task.FromResult(0);
}
public class Caller
{
public void DoCall()
{
ICallee xxx = new Callee();
// In my real case, the method just returns Task,
// so there is no type mismatch when assigning a result
// either.
xxx.DoSomethingAsync(); // This is where I had hoped for a warning.
}
}
}
After quite some difficulties with this problem I decided to create an Analyzer with code fix to solve it.
The code is available here:
https://github.com/ykoksen/unused-task-warning
It is also as a NuGet package that can be used as an analyzer for a project (when it is build):
https://www.nuget.org/packages/Lindhart.Analyser.MissingAwaitWarning/#
Furthermore it is also available as a Visual Studio Extension (for 2017). However this only analyses currently open files, so I'd recommend using the NuGet package. The extension is available here (or search for it in Visual Studio):
https://marketplace.visualstudio.com/items?itemName=Lindhart.missingAwaitWarning#overview
The code for the analyzer:
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyseSymbolNode, SyntaxKind.InvocationExpression);
}
private void AnalyseSymbolNode(SyntaxNodeAnalysisContext syntaxNodeAnalysisContext)
{
if (syntaxNodeAnalysisContext.Node is InvocationExpressionSyntax node)
{
if (syntaxNodeAnalysisContext
.SemanticModel
.GetSymbolInfo(node.Expression, syntaxNodeAnalysisContext.CancellationToken)
.Symbol is IMethodSymbol methodSymbol)
{
if (node.Parent is ExpressionStatementSyntax)
{
// Only checks for the two most common awaitable types. In principle this should instead check all types that are awaitable
if (EqualsType(methodSymbol.ReturnType, typeof(Task), typeof(ConfiguredTaskAwaitable)))
{
var diagnostic = Diagnostic.Create(Rule, node.GetLocation(), methodSymbol.ToDisplayString());
syntaxNodeAnalysisContext.ReportDiagnostic(diagnostic);
}
}
}
}
}
/// <summary>
/// Checks if the <paramref name="typeSymbol"/> is one of the types specified
/// </summary>
/// <param name="typeSymbol"></param>
/// <param name="type"></param>
/// <returns></returns>
/// <remarks>This method should probably be rewritten so it doesn't merely compare the names, but instead the actual type.</remarks>
private static bool EqualsType(ITypeSymbol typeSymbol, params Type[] type)
{
var fullSymbolNameWithoutGeneric = $"{typeSymbol.ContainingNamespace.ToDisplayString()}.{typeSymbol.Name}";
return type.Any(x => fullSymbolNameWithoutGeneric.Equals(x.FullName));
}
You have a few options:
This is the simplest "Caveman" solution use the built in VS search functionality (CTRL + SHIFT + F) search in Entire solution, also under Find Options click on the checkbox
Use Regular expression and use this regex: (?<!await|task(.*))\s([_a-zA-Z0-9\.])*Async\( It assumes you post fixed all your async method with the Async keyword and the method call is in one line. If it is not true then do not use it (or add the missing validations to the expression).
Use some 3rd party code analyzer tool, Nuget package. ReSharper is very popular and I believe it able to detect this problems or you can create your own rules.
My choice would be to use Roslyn (#Volker provided one solution). You can create your own rule set with code fix solutions (light bulb icon will show your code fix) so this is the best.
UPDATE: VS 2019 checks for this problem by default and gives warnings.
How to use Roslyn:
You have to install .NET Compiler Platform SDK: from here
Use VS 2017 Version 15.2 (or higher)
Create a new project File -> New -> Project, under the Extensibility group select: Analyzer with Code Fix (Nuget + VSIX) You have to target .NET Framework 4.6.2 to create this project.
You can copy paste the previous solution. Create
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class AsyncAwaitAnalyzer : DiagnosticAnalyzer
{ ...
}
class with logic, to detect the issue. And create
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(AsyncAwaitCodeFixProvider)), Shared]
public class AsyncAwaitCodeFixProvider : CodeFixProvider
{ ...
}
class to provide fixing suggestions (add await) to the problem.
After a success build you will get your own .wsix package you can install it to your VS instance and after a VS restart is should start pick up the problems.
In the end, we used roslyn to find all instances where a return value of Task or Task<> was ignored:
if (methodSymbol.ReturnType.Equals(syntaxNodeAnalysisContext.SemanticModel.Compilation.GetTypeByMetadataName(typeof(Task).FullName)))
{
// For all such symbols, produce a diagnostic.
var diagnostic = Diagnostic.Create(Rule, node.GetLocation(), methodSymbol.ToDisplayString());
syntaxNodeAnalysisContext.ReportDiagnostic(diagnostic);
}
if (((INamedTypeSymbol) methodSymbol.ReturnType).IsGenericType && ((INamedTypeSymbol) methodSymbol.ReturnType).BaseType.Equals(syntaxNodeAnalysisContext.SemanticModel.Compilation.GetTypeByMetadataName(typeof(Task).FullName)))
{
// For all such symbols, produce a diagnostic.
var diagnostic = Diagnostic.Create(Rule, node.GetLocation(), methodSymbol.ToDisplayString());
syntaxNodeAnalysisContext.ReportDiagnostic(diagnostic);
}
The compiler will emit warning CS4014 but it is only emitted if the calling method is async.
No warning:
Task CallingMethod() {
DoWhateverAsync();
// More code that eventually returns a task.
}
Warning CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.
async Task CallingMethod() {
DoWhateverAsync();
}
This is not terrible useful in your specific case because you have to find all the places where DoWhateverAsync is called and change them to get the warning and then fix the code. But you wanted to use the compiler warning to find these calls in the first place.
I suggest that you use Visual Studio to find all usages of DoWhateverAsync. You will have to modify the surrounding code anyway either by going through compiler warnings or by working through a list of usages.
You can add a specific warning in VS Project properties as one that throws a compilation error, like described here
You can add a semi-colon separated list of warning codes e.g. CS4014, and the compiler will fail to compile if you're not awaiting an async method.
Here's a screenshot with VS2017:
VS2017 configuration to throw compiler errors for warning CS4014
I found a great Visual Studio extension/NuGet package for this. Saves you having to create your own like other answers suggest.
https://github.com/ykoksen/unused-task-warning
Once I installed the Visual Studio extension I went to Analyze/Run Code Analysis/On Solution and it found several places where I had this issue.
I have a C# project. Is it possible to write code to the effect that "If an exception should occur while executing thus and such a task (and debugger is available), please break immediately, without unwinding the call stack."
Also, I just want to say, if this isn't possible, I'm fine with an answer to that effect.
You should take a look at the System.Diagnostics.Debugger class (https://msdn.microsoft.com/en-us/library/system.diagnostics.debugger(v=vs.110).aspx)
Using this class you can check to see if the debugger is attached and if it is you can break.
You could also wrap this in a static method on a utility class so you can use it easily
public static class DebuggerHelpers
{
[Conditional("DEBUG")]
public static void BreakIfDebugging()
{
if (System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Break()
}
}
}
The Conditional attribute (https://msdn.microsoft.com/en-us/library/system.diagnostics.conditionalattribute(v=vs.110).aspx) will cause all calls to this method to be omitted when the DEBUG is not defined (AKA Release).
If you want to leave the point where the exception was thrown and yet retain the call stack, you can do it only through logging the StackTrace AFAIK.
public static class Logger
{
...
public static string CurrentStackDefaultLog()
{
// the true value is used to include source file info
var l_CurrentStack = new System.Diagnostics.StackTrace(true);
return l_CurrentStack.ToString();
}
...
}
A good link for implementing this code is given in https://www.codeproject.com/Articles/223611/How-to-log-the-current-call-stack-in-NET by Daniele Mazzeranghi
Based on the other answers, it seems the answer to my question is that it can't be done.
In the current context we have two methods Start and Stop. These two methods are invoked from a function sequentially. There can be chances that a person invokes just Start() inside his method but forgets to invoke Stop(). e.g.
private void A()
{
Start();
//Buisness logic goes here
}
In this context when the code is compiled a warning or error needs to be displayed informing that for every Start() there should be a corresponding Stop(). Can somebody suggest ideas on how to go about implementing the same in C#?
The proper way of implementation would be
private void A()
{
Start();
//Buisness logic goes here
Stop();
}
I would suggest you change your pattern to take care of the Start and Stop without ever exposing it to the programmer.
Change your class implementing Start & Stop to implementing an Execute method instead and dont even expose the Start & Stop.
public class MyClass
{
private void Start(){} // old public method
private void Stop(){} // old public method
public void Execute(Action action)
{
Start();
action();
Stop();
}
}
Usage:
var impl = new MyClass();
impl.Execute(() => {
// do something in between start & stop
});
Evk gave a good hint, here is how I would do it in more detail:
Have a class (e.g. StartStop ) implement IDisposable
public class StartStop : IDisposable
{
public StartStop() { Start(); }
public void Dispose() { Stop(); }
protected void Start() { /*...*/ }
protected void Stop() { /*...*/ }
}
Make use of this class with using:
private void A()
{
using( var startStopCaller = new StartStopCaller() )
{
// Your code here
}
}
using will make sure Dispose() and subsequently Stop() will be called except for hard crashes.
This can be approached in many ways, with two primary directions:
If you're using the later versions of the .NET platform, and thus the Roslyn compiler (Defaults from VS2015 and onwards), you can look into writing a compiler plugin that checks this for you. Here are some resources:
Introduction to Scripting with the .NET Compiler Platform (Roslyn)
.NET Compiler Platform SDK
Probably a lot more out there, if you search for "Roslyn" or ".NET Compiler platform".
As some of the comments you got are pointing out, this could be fixed in your code and program design. This is most probably the "correct" way to approach this. Some examples:
Consider implementing IDisposable and use your class in a using statement - however, remember that stopping and disposing of an object might not be the same here. You should make an informed desicion about this with the knowledge you have about the inner workings of your program.
If you're calling these classes from elsewhere, you could let them implement an interface containing both your Start and Stop methods. And then let the calling class simply treat them as this interface, and make sure it calls both methods no matter which implementation it uses.
Re-architect your code to not depend upon running Start() and Stop() sequentially. This might require fundamental design changes to your program and how it works, but it might just be worth it. Both for readability and maintainability.
I'm trying to do unit testing on my C# ASP.NET project but need to run some initialization code for all tests:
[TestClass()]
public class Init
{
[AssemblyInitialize]
public static void initialize()
{
ContextProvider.setContext(new TestContext());
}
}
That's all I need to run before my tests, but it is not running. I tried to debug all my tests and put a breakpoint in that line but it is not hit. If I comment out the [AssemblyInitialize] and run one particular test that does not require this initialization, it runs successfully. However, with this line in, no tests run (and no breakpoints at all are hit)
What am I missing here?
Phil1970's helpful comment assisted in resolving the issue.
The problem with the initialize method is that it must receive TestContext (Microsoft.VisualStudio.TestTools.UnitTesting.TestContext). I couldn't find any guide/Microsoft documentation that explicitly states this, but the examples in this msdn page do have TestContext as input for the method.
After I added it, the tests ran successfully. On a side note, I had created a class to mock some data I needed and called it TestContext, which turned out to be a very poor name selection and made it more difficult to understand my mistake. I renamed it to APITestContext, here's my fixed initialization class.
[TestClass()]
public class Init
{
[AssemblyInitialize()]
public static void initialize(TestContext testContext)
{
ContextProvider.setContext(new APITestContext());
}
}
Here is (I think) the full list of things to check:
You should be using Microsoft.VisualStudio.TestTools.UnitTesting;
The class must be public
The class CANNOT be static or abstract
The class must be decorated with [TestClass]
The method must be public
The method MUST be static
The method must be decorated with [AssemblyInitialize]
The method must have the signature: void InitName(TestContext tc)
Don't know if it can be related but in the documentation it says
This attribute should not be used on ASP.NET unit tests, that is, any test with [HostType("ASP.NET")] attribute. Because of the stateless nature of IIS and ASP.NET, a method decorated with this attribute might be called more than once per test run.
Are you using that HostType?
We have an interesting issue occurring that I wonder if anyone may be able to shed light on. We are currently seeing the warning: "Method never reaches end or 'return' statement" on an event handler delegate callback that is rather odd.
Consider the following code (you can ignore the SCB_ functions, they are not relevant to the question):
public static class NativeBridge
{
private static UnityEventQueue _eventQueue;
private static bool _initialized;
public static void Init()
{
if (_initialized)
{
return;
}
_initialized = true;
SCB_SDKInit();
_eventQueue = UnityEventQueue.Instance;
_eventQueue.AppExiting += EventQueue_AppExiting;
SCB_registerReceivedSistrCallback(SistrReceived);
}
//Lots of other irrelevant code
private static void EventQueue_AppExiting(object sender, EventArgs e)
{
SCB_registerReceivedSistrCallback(null);
_eventQueue.AppExiting -= EventQueue_AppExiting;
SCB_SDKFinal();
_initialized = false;
}
}
The warning is on EventQueue_AppExiting. Here is the odd part. If I comment out the unregistration, _eventQueue.AppExiting -= EventQueue_AppExiting, the warning disappears.
We have tried a variety of "solutions" for what seems like a bug in the unreachability pass of the compiler:
Make the static class, non-static and adjust accordingly
Make the event in UnityEngineQueue static, and adjust accordingly
Place the event un-registration at the end of the callback method
Comment out the calls to the void SCB_ functions to sanity check
Various other Spaghetti at the wall solutions
All of the above yielded no change in the compiler's behavior. Our best guess is that the compiler detects the unregistration and thinks that because the delegate is removed, that it cannot complete execution at runtime, even though I believe the stack would have to continue execution even after removal because the invocation had already begun.
It does not seem like this is having any adverse effect in the application's execution, however it is difficult to debug due to the nature of the event's invocation conditions (Application Exiting).
What could the complier be seeing and/or what are we potentially doing wrong?
P.S.
For a bit of context, the class is static because it acts as an extern bridge to various platform specific libraries with a similar API. But that fact has little to do with the question, just to quell the "EWWWW Static Class" sentiment.
I think it is a bug as reported on this link:
https://bugzilla.xamarin.com/show_bug.cgi?id=42819
and here
https://bugzilla.xamarin.com/show_bug.cgi?id=41798
They report as fixed in version 6.2.0.259