views:

440

answers:

2

For an assignment I have to write a Tribool class in C# using a struct. There are only three possible tribools, True, False, and Unknown, and I have these declared as static readonly. Like this:

public static readonly Tribool True, False, Unknown;

I need my default constructor to provide a False Tribool, but I'm not sure how to go about this. I've tried Tribool() { this = False; } and Tribool() { False; } but I keep getting a "Structs cannot contain explicit parameterless constructors" error.

The assignment specified that the default constructor for Tribool should provide a False Tribool. Otherwise, a user should not be able to create any other Tribools. I don't really know what to do at this point. Any advice would be greatly appreciated. Thanks.

+5  A: 

As the error is telling you, you absolutely can not have a parameterless instance constructor for a struct. See §11.3.8 of the C# 3.0 language specification:

Unlike a class, a struct is not permitted to declare a parameterless instance constructor.

The language provides one for you known as the default constructor. This constructor returns the value of the struct where are fields have been set to their default value.

Now, what you could do is have the default value of the struct represent the False value. I'll leave the details to you.

Jason
Thanks much for the offer. Just wondering, if I didn't try to overwrite the default constructor, what would the default value be?I think I might not be completely understanding the assignment. Someone mentioned I should have a system for representing True/False/Unknown inside the Tribool class itself instead of using the public static readonly constants as values, and I shouldn't need the constructor since it's not doing anything meaningful...I'm not too sure what that means, and how I would define True/False/Unknown tribools. If you could explain it to me I'd really appreciate it. Thanks!
Alice
Yes, the key is find to a way to represent the `TriBool`. This is completely up to you and the "right way" is very much dependent on what else you have to do with `TriBool`. But some possibilities: 1. use `int` internally to represent the `TriBool` with "false" corresponding to 0, "true" corresponding to 1 and "unknown" corresponding to -1. Or, use two `bool` internally, one called, say `value` and the other called, say `unknown` with `value` representing "true" or "false" depending on if it is `true` or `false` but only if `unknown` is `false`, otherwise the instance represents "unknown."
Jason
Lastly, you should think about those representations, or whatever you come up with, in the context of the penultimate sentence of my answer.
Jason
Thanks so much for the help! I think I have an idea what to do now. One last question--If I were to define, say, the static Tribool True using int, I know to set its value to 1. But where would I declare that? I've tried public static readonly Tribool True {value = 1;} but it seems that I'm doing it wrong again. Could you give me a hint what I'm doing wrong? Thanks again.
Alice
What you're doing wrong is that you're trying to use object initializer syntax to initialize `value` but you probably haven't declared `value` as a property. That's fine. So you need a different approach. A couple options come to mind. 1. You could define a private constructor `TriBool(int value)` that sets the value of `this.value` and then say `public static readonly TriBool True = new TriBool(1);` 2. You could define a static constructor along the lines of `static TriBool() { True.value = 1; }` 3. Go ahead and define `value` as a property. Frankly, I favor option 1.
Jason
The second option worked great for me. Thanks so much, you saved me at least a couple hours I would've spent staring at the screen, lol.
Alice
@Alice: No problem; hopefully you learned in the process. :-)
Jason
`this` isn't read-only in structs. Strange but true.
Jon Skeet
Oh, and you can't write a parameterless constructor for a struct in C# - but you *can* use a struct which *does* have a parameterless constructor: http://msmvps.com/blogs/jon_skeet/archive/2008/12/10/value-types-and-parameterless-constructors.aspx
Jon Skeet
Indeed, Jon is right. "this" is a *variable* in a struct and a *value* in a class. If "this" were not a variable then how would you initialize the value type in the constructor?! What memory location would be changing?
Eric Lippert
@Jon Skeet, @Eric Lippert: Well, that's an embarrassing mistake. I'll blame that I was several hours removed from my last coffee. ;) Thanks for the correction. For reference, here's the StackOverflow question on this topic: http://stackoverflow.com/questions/1400031/meaning-of-this-for-a-struct-c
Jason
+6  A: 

Just to add a bit to Jason's answer: design your struct carefully so that the default value is meaningful. As an example, consider the Nullable<T> struct. The desired behaviour is that when you say

Nullable<int> x = new Nullable<int>(); // default constructor

that the resulting value is logically null. How do we do that? The struct is defined something like this:

struct Nullable<T> 
{
    private readonly T value;
    private readonly bool hasValue;
    public Nullable(T value)
    { 
        this.value = value;
        this.hasValue = true;
    }
    ...

So when the default constructor runs, hasValue is automatically set to false and value is set to the default value of T. A nullable with hasValue set to false is treated as null, which is the desired behaviour. That's why the bool is hasValue and not isNull.

Eric Lippert