Problem with MinValue<T> and MaxValue<T>!

Problem with MinValue<T> and MaxValue<T>!

Old forum URL: forums.lhotka.net/forums/t/4250.aspx


david.wendelken posted on Tuesday, January 29, 2008

Some while ago, I followed Rocky's advice and exposed my SmartDate (SmartInt16, SmartInt32, etc) variables as strings instead of SmartDates (and so on).

However, I just got bit while using the MinValue<T> rule.

public static bool MinValue<T>(object target, RuleArgs e) where T : IComparable
{
  
DecoratedRuleArgs args = (DecoratedRuleArgs
)e;
  PropertyInfo
pi = target.GetType().GetProperty(e.PropertyName);

  // Code barfs on the next line!
  T value = (T)pi.GetValue(target,
null);

  T min = (T)args["MinValue"];

  int result = value.CompareTo(min);

Here's the problem.  When T gets passed in, it gets passed in as, let's say, an Int32.

However, the property isn't an Int32, it's a String!  So, the cast to T into the value variable fails.

Anyone else hit this snag, and what was your solution?

simon_may replied on Tuesday, January 29, 2008

I was looking at the whole issue of SmartXXX's for the future. I have one question: why is it necessary when we now have nullables (accepted that if you are on .Net 1.1 it is the only way)

Simon

david.wendelken replied on Tuesday, January 29, 2008

simon_may:

I was looking at the whole issue of SmartXXX's for the future. I have one question: why is it necessary when we now have nullables (accepted that if you are on .Net 1.1 it is the only way)

Simon

I dunno.  I haven't tried incorporating them.

tmg4340 replied on Tuesday, January 29, 2008

simon_may:

I was looking at the whole issue of SmartXXX's for the future. I have one question: why is it necessary when we now have nullables (accepted that if you are on .Net 1.1 it is the only way)

There are a couple of issues with nullable types:

1. Many UI controls do not deal well (or at all) with the concept of a null value.  SmartDate (and the other smart controls) convert nulls to empty strings, or other suitable representations, for UI controls.  Yes, many of the third-party libraries have nullable support built in, but many of the built-in controls still don't.

(And don't ask why, because I don't know why, and it sure seems like an obvious thing that MS overlooked... Angry [:@])

2. Nullable types do not translate null values to a DBNull value for databases (and there is a difference).  SmartDate has that functionality built in.  Also something that seems rather obvious, since it would seem that the main impetus for nullable types is to work well with databases...

simon_may replied on Tuesday, January 29, 2008

Thanks TMG. You have saved me a lot of heartache. Typical of M$ provide a really good feature with a dirty great flaw in it. As I do a lot of code gen I can get around the probs.

Simon

tmg4340 replied on Tuesday, January 29, 2008

simon_may:

Thanks TMG. You have saved me a lot of heartache. Typical of M$ provide a really good feature with a dirty great flaw in it. As I do a lot of code gen I can get around the probs.

Simon

I will say that I have done little research to date on .NET 3.5, so it's possible that they've addressed this.  And I would hope that WPF - something else I haven't looked at much - deals with nullable binding better than WinForms.  I'd also be curious to know how LINQ manages this.

I can understand MS's "90% approach" to their UI controls - that's the one area where they really flex their IP muscle, and why give away all the really cool stuff?  But the missing functionality in the nullable types seems rather egregious to me.  MSDN says the best use for nullable types is to "represent things that exist or do not exist depending on the circumstance" - and then goes on to talk about "a nullable column of a database table [that] might exist in one row of the table but not another."  Honestly, that statement makes no sense to me, though it seems to shed a little light on why nullable types work the way they do (and further shows that MS wasn't thinking too much about the problem they were trying to solve.)  Lastly, while the "ToString()" implementation of nullable types returns an empty string if the variable has no value, that's not very helpful in most circumstances.

- Scott

simon_may replied on Tuesday, January 29, 2008

TMG, Thanks for the info. Seems that the guy form M$ has never written a business app otherwise he might talk some sense rather a party line that makes no sense in the real world.

Simon

ajj3085 replied on Tuesday, January 29, 2008

3.  It allows you to sort all "null" values as minimums or maximums, depending on the EmptyIsMax value.  So maybe in one case your empty SmartDate should appear first in ascending order, but in another case, it should appear at the end of the list.

RockfordLhotka replied on Tuesday, January 29, 2008

SmartDate is not intended to solve the null problem. It is intended to solve the empty problem. There’s a difference.

 

·         A null value indicates a value not yet supplied.

·         A non-null value indicates a value that has been supplied – which might be an actual value or an empty value.

·         An empty value is a supplied value, that is empty. It could be infinitely small or large relative to other values.

 

Null and Empty are not the same thing – we just often use the “null” concept to represent Empty because there is no Empty concept available.

 

Look at SQL – no way to indicate an Empty date value other than either using null or picking some arbitrary real date to be the empty placeholder. Yet the idea of an empty date is central to a lot of applications. Fortunately, very few applications actually need the null concept at all, so it is fine to use null as a placeholder for Empty.

 

But if you are writing one of those few apps where null actually matters, then you are in a spot…

 

This null thing is one of my pet peeves though. People misuse it all the time. Null exists to support the idea that a user hasn’t supplied a value – so null <> “” and null <> 0 and null <> false. The “”, 0 and false values are actual values, which implies they were supplied by the user. Null is used to indicate something not supplied (yet anyway).

 

(I’m ignoring the foreign key use of null in a database, because that’s a concept unto itself and is separate from the use of null in value columns/properties)

 

Very few apps actually have this business requirement. Those apps that do have this requirement must, by definition, have some UI representation so the user knows whether a value has been supplied or not. With numerics this is relatively easy – 0 is visibly different from “”, so you can use “” to represent null. But for strings it is hard, because there’s no clear way to represent a null in a textbox. So you probably need an associated checkbox control, or a specialized textbox that can show text like “<null>” in italics or something to denote a non-supplied value.

 

The same is true for a date if you want the user to type it in as freeform text. Again, the “” can be Empty, but then it can’t be null. So you have the same problem as with regular text.

 

Boolean isn’t too bad if you have a tri-state checkbox. That way you can show true/false/unknown and have unknown be proxy for null.

 

Rocky

 

 

From: ajj3085 [mailto:cslanet@lhotka.net]
Sent: Tuesday, January 29, 2008 1:38 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] Problem with MinValue<T> and MaxValue<T>!

 

3.  It allows you to sort all "null" values as minimums or maximums, depending on the EmptyIsMax value.  So maybe in one case your empty SmartDate should appear first in ascending order, but in another case, it should appear at the end of the list.


david.wendelken replied on Tuesday, January 29, 2008

Yo!  Everybody!  Big Smile [:D]

Y'all can start all the threads you want, but could we keep this one on track?

I'm not able to switch to v3.5 just yet.   Crying [:'(]

So I'm looking for a v2 solution to the immediate problem I posed...

 

Thanks!!!!!!!

 

RockfordLhotka replied on Tuesday, January 29, 2008

The CoerceValue() code should back-port, that’s why I brought it up.

 

Rocky

 

From: david.wendelken [mailto:cslanet@lhotka.net]
Sent: Tuesday, January 29, 2008 2:14 PM
To: rocky@lhotka.net
Subject: Re: [CSLA .NET] RE: Problem with MinValue<T> and MaxValue<T>!

 

Yo!  Everybody!  Big Smile <img src=">

Y'all can start all the threads you want, but could we keep this one on track?

I'm not able to switch to v3.5 just yet.   Crying <img src=">

So I'm looking for a v2 solution to the immediate problem I posed...

 

Thanks!!!!!!!

 



JoeFallon1 replied on Tuesday, January 29, 2008

David,

I think since you control both ends of this that you just have to line them up correctly.

You said T gets "passed in" as an Integer.

That means your declaration is something like this:

ValidationRules.AddRule(AddressOf MinValue(Of Integer), New MinValueRuleArgs(Of Integer)("Price", 0))

Why not change it to:

ValidationRules.AddRule(AddressOf MinValue(Of String), New MinValueRuleArgs(Of String)("Price", 0))

Joe

david.wendelken replied on Tuesday, January 29, 2008

JoeFallon1:

David,

I think since you control both ends of this that you just have to line them up correctly.

You said T gets "passed in" as an Integer.

That means your declaration is something like this:

ValidationRules.AddRule(AddressOf MinValue(Of Integer), New MinValueRuleArgs(Of Integer)("Price", 0))

Why not change it to:

ValidationRules.AddRule(AddressOf MinValue(Of String), New MinValueRuleArgs(Of String)("Price", 0))

Joe

Because if I evaluate it as a string, 11 is less than 2. :)

 

tmg4340 replied on Tuesday, January 29, 2008

david.wendelken:

Some while ago, I followed Rocky's advice and exposed my SmartDate (SmartInt16, SmartInt32, etc) variables as strings instead of SmartDates (and so on).

However, I just got bit while using the MinValue<T> rule.

public static bool MinValue<T>(object target, RuleArgs e) where T : IComparable
{
  DecoratedRuleArgs args = (DecoratedRuleArgs
)e;
  PropertyInfo pi = target.GetType().GetProperty(e.PropertyName);

  // Code barfs on the next line!
  T value = (T)pi.GetValue(target, null);

  T min = (T)args["MinValue"];

  int result = value.CompareTo(min);

Here's the problem.  When T gets passed in, it gets passed in as, let's say, an Int32.

However, the property isn't an Int32, it's a String!  So, the cast to T into the value variable fails.

Anyone else hit this snag, and what was your solution?

What about using Convert.ChangeType()?  It returns an Object, but it should be a castable object at that point...

- Scott

david.wendelken replied on Tuesday, January 29, 2008

tmg4340:

What about using Convert.ChangeType()?  It returns an Object, but it should be a castable object at that point...

That's an incredibly good idea!

// dataType is a new parameter I introduced so I could know type T is.  I would love to not use it!

Type t = Type.GetType(dataType);

T value = (T)Convert.ChangeType(pi.GetValue(target, null), t);

T min = (T)Convert.ChangeType(args["minValue"],t);

The above code works, provided I supply the dataType parameter with the correct class name.

I would prefer to have it smart enough to just use T, but I haven't figured out the syntax...

Can't figure out the syntax to ask a static method what T is, either, which would be another way to do this without an extra dataType parameter.

Ideas?

RockfordLhotka replied on Tuesday, January 29, 2008

I wasn't really following this thread, so I don't know the exact issue. But if you are trying to coerce a value to a different type, that's tricky due to the various edge cases.

I spent a lot of time for CSLA 3.5 addressing this issue, culminating in the CoerceValue() methods in the Csla.Utilities class. I don't know that it solves every case, but it solves all the test cases I threw at it, and the ones that were provided by people struggling with coercion in DataMapper.

RockfordLhotka replied on Tuesday, January 29, 2008

RockfordLhotka:

I wasn't really following this thread, so I don't know the exact issue. But if you are trying to coerce a value to a different type, that's tricky due to the various edge cases.

I spent a lot of time for CSLA 3.5 addressing this issue, culminating in the CoerceValue() methods in the Csla.Utilities class. I don't know that it solves every case, but it solves all the test cases I threw at it, and the ones that were provided by people struggling with coercion in DataMapper.

I should also point out that SmartDate in 3.5 now has a whole lot of type coercion functionality of its own - including a set of cast operators and a TypeConverter implementation. CoerceValue() relies on your type implementing some/all of that infrastructure to work, so if you are using your own custom SmartXYZ types, they will probably need to have similar operators and a TypeConverter (at least for conversion to/from string).

tmg4340 replied on Tuesday, January 29, 2008

david.wendelken:
tmg4340:

What about using Convert.ChangeType()?  It returns an Object, but it should be a castable object at that point...

That's an incredibly good idea!

// dataType is a new parameter I introduced so I could know type T is.  I would love to not use it!

Type t = Type.GetType(dataType);

T value = (T)Convert.ChangeType(pi.GetValue(target, null), t);

T min = (T)Convert.ChangeType(args["minValue"],t);

The above code works, provided I supply the dataType parameter with the correct class name.

I would prefer to have it smart enough to just use T, but I haven't figured out the syntax...

Can't figure out the syntax to ask a static method what T is, either, which would be another way to do this without an extra dataType parameter.

Ideas?

I will say one other thing - the Convert class expects that the value in question implements IConvertible.  So you might want to make a modified version of MinValue<T> to include the IConvertible requirement, in addition to IComparable.  That will help you catch problems at compile-time.

As for your other question - can you just use "T.GetType()"?  Given no other hints, generic placeholders always equate to an Object.  Or you could try "typeof(T)" as well.

- Scott

david.wendelken replied on Tuesday, January 29, 2008

tmg4340:

I will say one other thing - the Convert class expects that the value in question implements IConvertible.  So you might want to make a modified version of MinValue<T> to include the IConvertible requirement, in addition to IComparable.  That will help you catch problems at compile-time.

Thanks!  I'll look into it.

tmg4340:

As for your other question - can you just use "T.GetType()"?  Given no other hints, generic placeholders always equate to an Object.  Or you could try "typeof(T)" as well.

I feel like SUCH a goober.  I REALLY thought I had tried typeof(T) and it wouldn't compile.  But it works great.  Need vacation.  Bad.

I'm pretty sure T.GetType() won't work.

tmg4340 replied on Tuesday, January 29, 2008

david.wendelken:
I feel like SUCH a goober.  I REALLY thought I had tried typeof(T) and it wouldn't compile.  But it works great.  Need vacation.  Bad.

I'm pretty sure T.GetType() won't work.

I know how you feel - I haven't had a "real" vacation in a couple of years.

(It's funny - that's about how old my son is... Smile [:)])

I started my .NET life as a VB.NET programmer, and even after more than a year of C# programming, I still can't get it straight in my head when to use "typeof" and when to use "GetType" in these instances...

- Scott

Copyright (c) Marimer LLC