14

Simple question, how do you force the C# compiler to throw a compilation error.


Update: Perhaps it's better to use an Assert.Fail() instead?

I have a custom-attribute that should only be applied to ONE member of a class. Inside of my other class' static method it looks for that one member and I want it to fail (not throw an exception) if more than one member had the attribute applied to it.

public class Foo
{
    [MyCustomAttribute]
    public String FooString { get; set; }

    [MyCustomAttribute]
    public String OtherFooString { get; set; }
}


public class Bar<T>
    where T : class, new()
{
    static Bar()
    {
         //If more than one member of type Foo has MyCustomAttribute
         //applied to it compile error or Assert.Fail()?
    }
}
user2864740
  • 60,010
  • 15
  • 145
  • 220
myermian
  • 31,823
  • 24
  • 123
  • 215
  • Assert.Fail() doesn't throw a compiler error, it throws an assert at runtime – Matt Jun 07 '11 at 13:50
  • @Matt: I know, i was wondering what would be better.... a compilation error or Assertion error. – myermian Jun 07 '11 at 13:55
  • 1
    Ah. Well, then given your new information, I'd say that throwing an exception at runtime is a better option. – Matt Jun 07 '11 at 13:58
  • @m-y as @Matt pointed, it depends; for a static code check - compilation error is generally first choice, because what you know at compile time is better to handle now rather than in unknownly situation; BUT the control you are trying to handled is not purely static check - you can decorate another member of `Foo` at runtime and then compilation error is simply not raised. – laika Aug 12 '13 at 16:09
  • If you have control over consuming code base (that is, the assembly in which MyCustomAttribute is defined is YOUR assembly), you can write a custom action to inspect all .cs files and check that only one instance of the attribute is present in a file in non-commented areas. With vs 2015 you can also use code analyzers with roslyn. – zaitsman Jul 10 '16 at 05:34

4 Answers4

33

You can use a diagnostic directive:

#error Oops. This is an error.

or for just a warning:

#warning This is just a warning.

You'd normally want to put these in conditional blocks, I'd expect...

EDIT: Okay, now you've updated your question, you simply can't do this at compile-time. Your suggestion of using Assert.Fail punts the problem to execution time.

I would suggest you write unit tests to detect this (iterate over all the types in the assembly, and check that the attribute has only been applied at most once per type).

EDIT: In 2016... while Code Analysis as suggested by the OP isn't actually a compiler error, now that Visual Studio uses Roslyn, it's feasible to hook into the compiler and genuinely get an error from the compiler, using a Roslyn code analyzer. However, I would still personally prefer unit tests for this, as then the code could be built and tested by anyone, regardless of whether they had the Roslyn analyzer installed. There's still no way of validating this with a "purely vanilla" C# compiler.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    Yep. I have a strict condition for this. Though, I wonder... is it better to do this or an Assert.Fail()? The portion that I want to error out in is within a static constructor. – myermian Jun 07 '11 at 13:42
  • 1
    I left a comment above too, you can't use Assert.Fail() because that won't throw a compiler error, it throws a assert at runtime. – Matt Jun 07 '11 at 13:52
  • @myermian: Neither `Assert.Fail` nor `#error` does what you want. Use a unit test instead. – Jon Skeet Jun 07 '11 at 13:56
  • Hmm... now that I think about it I don't think I can get the logic I need to throw a compilation error with my scenario. Is it even possible to do what I want based on my example? – myermian Jun 07 '11 at 13:58
  • More info on the directives: [#error](http://www.dotnetperls.com/error), [#warning](http://www.dotnetperls.com/warning) – Devendra D. Chavan Jun 07 '11 at 14:00
  • @zaitsman: Care to give more details? As it is, that isn't a constructive comment. – Jon Skeet Jul 10 '16 at 06:38
  • @JonSkeet I did, in the comment to the original question. Basically, if this is your code you worry about (not a library attribute for others to use) you can write a custom action to raise compile errors based on any condition you want. – zaitsman Jul 10 '16 at 07:46
  • @zaitsman: The custom action wouldn't be a compiler error - and as you say, it assumes you control consuming code. The Roslyn approach is fine, but to call this answer "simply wrong" because it was written several years before Roslyn came out is pretty rude, IMO. I've updated my answer to talk about Roslyn, but I still think it's an entirely reasonable answer. (And if you're going to criticize an answer, the criticism should include the reasons, rather than them being somewhere else. Why assume that someone reading the comment on the answer would have read all the rest of the comments?) – Jon Skeet Jul 10 '16 at 08:15
  • @JonSkeet If the error is thrown during compilation both in MSBuild and Visual Studio, doesn't it meet the criteria? I guess maybe i misunderstood the question then, because i don't see why you would need a genuine CS error for something that you want to arbitrarily limit; i can totally see how you want to prevent the team mates making mistakes like this, and for that goal a custom action is perfectly reasonable IMO. – zaitsman Jul 10 '16 at 08:16
  • @zaitsman: I don't think it meets the criterion of being a compiler error, which was the question asked. Note that a unit test would also prevent team-mates from making an error, unless your team-mates are in the habit of checking in code without running tests. – Jon Skeet Jul 10 '16 at 08:17
  • @zaitsman: I'd also say that an error thrown *during the build* isn't the same as an error thrown *during compilation*. The build consists of more than compilation, and custom actions aren't part of compilation (although Roslyn analyzers are). – Jon Skeet Jul 10 '16 at 08:22
  • @JonSkeet Okay, so semantically speaking i am wrong, so i apologise. As far as unit tests are concerned - when this attribute is used on a new model by a new developer with no unit tests covering a behaviour in the new type, how would the unit test cover it? Granted, you could write one to load all assemblies and all types and Assert on that, but it seems a bit wasteful to me. And yes, once your unit tests reach thousands and take tens of minutes to execute you might let the CI send you a 'it failed e-mail' instead of waiting on them to check in (although that is a bit off-topic). – zaitsman Jul 10 '16 at 08:25
  • @zaitsman: Your custom action requires the developer to opt into it as well though, so I don't see the difference. (And if the bottleneck in your unit tests is reflecting over the assemblies, that suggests you don't have enough other unit tests. It really doesn't take long to do this sort of reflection.) My main issue is the unconstructive nature of your initial comment though. I would urge you to reflect on that and consider future critical comments more carefully. (Criticism is fine, of course - but it should be *useful* and also take account of context, such as the year of posting...) – Jon Skeet Jul 10 '16 at 08:30
4

Over a year after this question has been asked, I realized an alternative solution to unit tests: Code Analysis.

If no Code Analysis rule exists, you could even roll your own. Just make sure to set the rule as Error instead of Warning (or make sure to treat warnings as errors). Here's an image of that screen:

Code Analysis Error

Of course, I still preferred to use a Unit Test in this particular case, but I can definitely see Code Analysis as an alternative solution, especially for things such as custom naming (i.e. All my classes should end in 'Foo' or throw an error).

Emilio Brandon Flanagan
  • 1,142
  • 1
  • 12
  • 13
myermian
  • 31,823
  • 24
  • 123
  • 215
  • 1
    Is it possible for custom Code Analysis rules to be included in the project or solution so that when another developer is working on the same project/solution from their workstation, they will not have to manually setup the rule as well? – crush Sep 08 '14 at 13:44
  • @crush, did you ever figure out if it's possible to embed the rules in a project? – Jonathan Wilson Jul 25 '16 at 23:02
  • 2
    @JonathanWilson: I can tell you that now that we have Roslyn, it is possible to include .NET Analyzers in projects as NuGet packages. – myermian Jul 25 '16 at 23:39
3

Simple Answer: Ask it to compile something syntactically invalid.

Justin Niessner
  • 242,243
  • 40
  • 408
  • 536
1

I don't believe this can be done, because it is not possible to tell compiler to check for custom errors. Of course, you can use #error, like Jon said, but there is no way to add the condition itself. Because #error will cause error each and every time.

Only thing you can be sure about is to check at runtime and then throw exception.

Edit: Damn, getting to question after it was heavily edited.

Euphoric
  • 12,645
  • 1
  • 30
  • 44
  • Eh, sorry about that. After I posted the question I realized that perhaps I should give more information as a sort of a "... and should I do it that particular way?" addition. And it's a good thing I did too because Jon is right, Unit Testing is what will help keep things in line in this situation. – myermian Jun 07 '11 at 14:05