views:

240

answers:

4

I'm working on a method that will accept a StreamWriter, either a string or a nullable value, and a length, and write the value to the StreamWriter, padded to the length. If the value is null, I want to write spaces. I want to do something like the following oversimplified example, which is just for demonstration purposes.

public void FixedWrite(StreamWriter writer, string value, 
        int length) {
    if (value == null) value = "";
    value = value.PadRight(length);
    writer.Write(value);
}

public void FixedWrite<T>(StreamWriter writer, T value, 
        int length) where T : Nullable /* won't work of course */ {
    string strVal;
    if (!value.HasValue) strVal = null;
    else strVal = value.Value.ToString();
    FixedWrite(writer, strVal, length);
}

I could have overloads for all the different underlying types (they're all dates, ints, bools and decimals), but I wanted to see if I could get the generic version working. Is there some way of getting T to always be a Nullable<> type, and to access the Nullable<> properties (HasValue and Value)? Or should I just stick with the underlying-type-specific overloads?

This question poses a similar problem, but in my case the values are already nullables and I just want to write out the value if it has one and spaces if it doesn't.

+10  A: 

Try something like this:

public void FixedWrite<T>(StreamWriter writer, Nullable<T> value, int length) 
    where T : struct
{
    string strVal;
    if (!value.HasValue) strVal = null;
    else strVal = value.HasValue.ToString();
    FixedWrite(writer, strVal, length);
}
Andrew Hare
Of course. I was making it harder than it needed to be. Thanks.
John M Gant
This works, but you *can* still pass a nullable parameter to the generic method.
Oplopanax
A: 

The simple answer is no.

Even tho it is a struct, it's been specifically disallowed in the CLR due to recursion issues that could arise.

Nullable<Nullable<Nullable<etc.>>>()
Jaimal Chohan
It's possible to do what he wants to do which is to pass a nullable type as a generic parameter. It's also possible to test that type for null since Nullable is no longer a value type, just test ==null
Oplopanax
+1 for the helpful information. It is possible to pass a nullable as a generic parameter, as Oplapanax points out, but Jaimal's right, you can't define a generic method to specifically restrict to nullables, which is what I was after. Not sure why this was downvoted, because it is a helpful and technically correct answer to my question.
John M Gant
+3  A: 
public void FixedWrite<T>(StreamWriter writer, Nullable<T> value, int length)

Anything wrong with that?

JSBangs
In order for this to compile you must constrain `T` as a value type, please see my answer.
Andrew Hare
Thanks. I was too lazy to fire up VS to find out what the compiler actually says :).
JSBangs
+1  A: 

No need to test for HasValue, since Nullable is a struct, you can impose the "new()" constraint:

public void FixedWrite<T>(StreamWriter writer, T value, int length) where T : new()
{
    // just test for NULL
    string strVal = (value==null) ? string.Empty: value.ToString();
    FixedWrite(writer, strVal, length);
}

Now you call it like so (using the "?" notation is the key):

FixedValue<System.DateTime?>(....)
Oplopanax
But I won't be able to access the HasValue and Value methods of value because the compiler won't know it's a nullable, right?
John M Gant
correct; testing for NULL means it has no value. If it's not null, it has a value.
Oplopanax
sorry, edited the code to remove that statement, made it clearer hopefully.
Oplopanax
Correction: `Nullable<T>` is a value type, not a reference type.
Andrew Hare
Andrew: sorry, you are right, nullable is a struct. the New() constraint still holds.
Oplopanax
OK, I get it. I didn't think this would work, but I tested it out and it does. Andrew Hare's method solves my specific problem in a type-safe way, which is what I was looking for (your approach *allows* value to be nullable, whereas Andrew's requires it). But this answer helped me enhance my understanding of nullables, so +1 and thanks.
John M Gant
The effect of specifying the new() constraint is that the type passed in must be comparable to NULL. In this sense, it can only be a struct or class, which both implement ToString(), so for type safety I think you're covered. It suffers from the constraint that it has to have a public, parameterless constructor however. There may be another way to limit this if you study constraints: http://msdn.microsoft.com/en-us/library/d5x73970(VS.80).aspx
Oplopanax