Fog Creek Software
Discussion Board




Welcome! and rules

Joel on Software

C#: User defined datatypes

Hi there,

Just want to know if I can define my own datatypes in C#.

Lets say I want to make my own double float and define that as a datatype called 'Percentage'.

The simple method would be to inherit the standard System.Double object and claim it as mine, e.g.

public class Percentage: double {}

Unfortunately the compiler will complain bitterly stating that I cannot inherit a sealed class. [A class which is final, and cannot be derived further]

Then using the 'using' keyword:

using Percentage = System.Double;

compiles wonderfully, and I had high hopes.

Unfortunately when running it through with reflection, the datatype returned is not a 'Percentage' but the base type which is 'System.Double'.

Which defeats the purpose of me declaring the new datatype in the first place, i.e. 'using' just did a string replace of all the occurrences of 'Percentage' with 'System.Double'.

Here is what I do in Delphi:

type
  Percentage = type Double;

Using RTTI/Reflection reveals that my Percentage type is a
first class citizen of datatype land, i.e. its recognized as is,
not as a double of a double.

Is there any other way of me making my own datatype in C#?

Regards,

Yoon Kit.

Yoon Kit Yong
Monday, January 26, 2004

I believe you would have to define your own value-type (struct in C#, structure in VB.Net) that contains a double

Samuel Jack
Tuesday, January 27, 2004

void FooBar(double percentage);

See now, doesn't that feel better? :)

Seriously, what you're doing is a classic OO beginner mistake. Percentage is a name, not a type. If you have operations that go along with it, then make it a full fledged type, not just an alias for double.

Brad Wilson (dotnetguy.techieswithcats.com)
Tuesday, January 27, 2004

I thought about making my own struct but I thought it was abit too much work. Doing all the operator overloadings (which are identical to double) and such doesnt really justify the luxury of having my own datatype. But yeah, that was one option.

> Percentage is a name, not a type
hmm, consider properties like:

public class Order
{
  public Percentage TaxRate {}
  public Percentage Discount {}
  public Percentage CommisionRate {}
    :
}

works so much better than 'double' dont you think?

I was thinking about Attributes to indicate
that the property is a different type of double.

    :
  [DataTypeAttribute( dtPercentage )]
  public double TaxRate {}
    :

What do you guys think?

yk.

> void FooBar(double percentage);
percentage is a very generic and non-descriptive word,
and Id be surprised any seasoned programmer would use it as a parameter or variable name.

Yoon Kit Yong
Tuesday, January 27, 2004

Sorry, but this is what I'd use:

public class Order
{
  public double TaxRatePercentage {}
  public double DiscountPercentage {}
  public double CommisionRatePercentage {}
    :
}

Brad Wilson (dotnetguy.techieswithcats.com)
Wednesday, January 28, 2004

Hi,

The question of whats the best property name
to use is not really the question.

The reason why I need to use my own datatype
is because I want to apply Reflection / RTTI on my
objects to do repetitive stuff.
e.g. Automatically creating the input fields
for the UI with the correct input formats:
straight floats, currencies, percentages, integers, etc.
Or type check the db tables automatically.

This is why Im asking this question about
my own datatypes.

yk

[btw, would you have your class defined as such:

public class Order
{
  public double TaxRatePercentage {}
  public double OrderWeightFloat {}
  public integer NumberOfItemsInteger {}
  public string  InvoiceNumberString {}
  public DateTime OrderDateDate {}
    : 
}

or

public class Order
{
  public Percentage TaxRate {}
  public double OrderWeight {}
  public integer NumberOfItems {}
  public string  InvoiceNumber {}
  public DateTime OrderDate {}
    : 
}]

Yoon Kit Yong
Thursday, January 29, 2004

From my very limited understanding.  All data types in C# except the object type are "value" types (stored on the stack). Objects are "reference" types (stored in the heap). I believe this implies that you cannot perform object type operations on anything that is not derived directly from object.

I suspect this leaves you with no option but to encapsulate the data type in a object.

Peter WA Wood
Thursday, January 29, 2004

Actually, all .NET types are implicitly derived from System.Object, and therefore reference types and stored on the managed heap; EXCEPT those that are derived from the special subclass System.ValueType (declared as struct instead of class in C#) which are stored on the stack, except when "boxed" (converted to Object) or stored as part of reference types (fields, array elements).

Also, value types support all operations that reference types support, except for user-defined default constructors and inheritance. They have different assignment semantics, though -- basically automatic cloning.

Chris Nahr
Thursday, January 29, 2004

public class Order
{
  public double TaxRatePercentage {}
  public double OrderWeightInPounds {}
  public integer ItemCount {}
  public string InvoiceID {}
  public DateTime OrderDate {}
}

Brad Wilson (dotnetguy.techieswithcats.com)
Thursday, January 29, 2004

What Samuel said + implicit conversion operators, so you can use your type as an ordinary double. Something like:

struct Percentage
{
  private double _d;

  public Percentage( double d )
  {
      _d = d;
  }

  public static implicit operator double( Percentage p )
  {
      return p._d;
  }

  public static implicit operator Percentage( double d )
  {
      return new Percentage( d );
  }
}

Danko
Friday, January 30, 2004

Wow, Danko, ThankYou!

Its just what I need.
I guess Ill have to do some reading up on
the keyword 'implicit' ... Ive never seen it before!

I tried this struct out.
It worked flawlessly with a Console.WriteLine(vPerc).

But with Reflection, Im not sure why
GetValue of the Property e.g. TaxRate
returns the ClassType string, and not the d.value.

So what I did was added:

    public override string ToString()
    {
        return _d.ToString();
    }

Am I correct in doing so?
What other caveats do you forsee?

Thanks again, and Regards!

yk.

Yoon Kit Yong
Friday, January 30, 2004

No, Brad is correct. The only reason to create your own Percentage type would be if you wanted it to support additional functionality over a regular double (add new methods or validate that the number is always greater than zero or something like that). From your example, you don't seem to be doing that; so defining a separate data type is a classic OO misunderstanding. It simply doesn't buy you anything except extra work.
  Put it this way: if a method in your Order class returns a DateTime that represents the payment date of the order, would you define a new DateTime class called PaymentDate? No, of course not. A regular DateTime would work perfectly fine. Same thing here--the fact that your double happens to represent a percentage doesn't change anything. From an OO point-of-view, there's no reason to make a new type.

Anon. Coward
Sunday, February 01, 2004

> so defining a separate data type is a
> classic OO misunderstanding.

I dont pretend to be a OO guru, and I am
definitely not a purist. However I think I have
strong justifications in making my own datatype
even though it may be reproducing a datatype of
a double.
If you would read my 3rd post (29th Jan)
regarding the usage of this particular datatype,
you may see the practical necessities of this
un-OO (allegedly ;-) request.

>> .. I want to apply Reflection / RTTI on my
>> objects to do repetitive stuff.
>> e.g. Automatically creating the input fields
>> for the UI with the correct input formats: ...
>> Or type check the db tables automatically.

Usage of my class and its attributes with
richer type information can go a long way for me.

> new DateTime class called PaymentDate?
> No, of course not.
> A regular DateTime would work perfectly fine.
Totally agreed with you.
However the 'Percentage' datatype is abit more
descriptive to a standard double than a DateTime to
a PaymentDate.

For example, the formatting would be different (display %),
decimal point precision could be different (restrict to 2dec),
whether its data is converted to 100th of a point
(e.g. 90% could be stored as 90.00 or as 0.90)
which could affect calculations.
I could think of many other different behaviours
this new type can do, but the most obvious,
and most useful is the class description.

public class Order
{
  public double OrderWeight {}
  public Percentage TaxRate {}
    :
}

Straight away, anybody will know what 'type'
TaxRate is, being a programmer, architect or
business person. And that I feel (very subjective)
makes using my own datatype very important
and alot more OO in usage.

> From your example, you don't seem to be doing that
Remember, this is just an example,
I havent even touched on implementation.
I guess a trivial one would be:

  public override ToString() { return _d.ToString() + "%" }

>> classic OO beginner mistake.
>  classic OO misunderstanding
Just out of curiousity, are there any sites
which list definite classic No-nos for OOP?
So I can read 'em and not offend you experts.

yk.

Yoon Kit Yong
Tuesday, February 03, 2004

Again, what you're talking about is really blurring the line between two things that shouldn't be blurred. Having data presentation aspects filter into any other part of your system than the presentation layer is not good design, in my experience.

Brad Wilson (dotnetguy.techieswithcats.com)
Tuesday, February 03, 2004

> Having data presentation aspects filter into any
> other part of your system than the presentation
> layer is not good design.

Agreed.
But again, the appending of '%' in the ToString()
function was a very trivial example, because
Anonymous coward wanted one.

This would be a less trivial one, which will definitely
belong in the business object level:

public Currency TotalTax()
{  return  TaxableTotal() * (TaxRate);  }
or
{  return  TaxableTotal() * (TaxRate/100);  }

If a programmer was clear that TaxRates
store their values as 0.30 to represent 30%
then the first return value would be correct.
The usage of this Percentage datatype is now standard,
and much more intuitive to all.
If we used 'double' the confusion will exist.

> blurring the line between two things
> that shouldn't be blurred.

Is it so bad to have a class with more meta-information
so that we can help in both presentation and persistence layers?
e.g. If it is Percentage type, then this info
can be used by the UI layer to append the '%' in the edit
box, and the db layer can check that the field type
is Numeric(16,2) [or whathaveyou..].

In fact its not blurring the lines at all.
It makes the class definition very much clearer,
and this information now becomes obvious
in all layers and this is a very useful thing.

yk.

Yoon Kit Yong
Tuesday, February 03, 2004

"Is it so bad to have a class with more meta-information
so that we can help in both presentation and persistence layers?"

Yes.

If you believe otherwise, then I guess we just have to agree to disagree. In my experience, it absolutely, positively does not belong, and I would tell any of my engineers to decouple it as soon as possible.

Brad Wilson (dotnetguy.techieswithcats.com)
Friday, February 06, 2004

Hi Brad,
Thanks for the suggestion to agree to disagree.
I am still curious about your position,
so could you give me an example on
why you are so against meta-information in classes,
or otherwise some links so that I can read up
on your programming philosophies?

Is there anybody else out there who has some
insight in this area?

Regards, and it has been stimulating ...

yk.

Yoon Kit Yong
Friday, February 06, 2004

I'm not against meta-information in classes. What I'm against is the idea that your data layer class should include presentation meta-information. You're tearing down the layer separation, and in my experience, you will severly regret it at some point in the future.

Here's an example. Let's say that I write something like:

public class Percentage
{
  private double _val;

  public string FormatForDisplay()
  {
    return string.Format("<b>{0}%</b>", _val);
  }
}

There's two huge problems here.

First, the most obvious, is that you not only tied in presentation logic, but presentation logic that's specific to a particular type of presentation system (HTML).

Second, you presume that displaying for formatting will always want to be in whole numbers, followed by a percent sign. How could you possibly make the decision that every time, everywhere, that you display the number, it must be like that, in every presentation system ever devised, in every conceivable usage?

That's a just plain bad decision, no matter how convenient you might think it sounds today.

But you're not me. Feel free to do what you want.

Brad Wilson (dotnetguy.techieswithcats.com)
Friday, February 06, 2004

> return string.Format("<b>{0}%</b>", _val);
I dont think anybody in their right mind
would want to even consider using this code
anywhere. It clearly restricts the use of this
object with HTML as the presentation layer.
This is obviously wrong, and is too extreme
an example.

> data layer class should include
> presentation meta-information.
This is not the data layer, its the
Problem Domain/Business Object Layer.
The ToString() is not presentation meta-information at all.
Its just the standard 'information'.
Meta-information are things like what datatype this is,
what sort of inputs this type prefers, suggestions
on how to format it (but not the format itself), etc...
Data Layer is the logic which persists the objects.

> always want to be in whole numbers,
> followed by a percent sign.
> How could you possibly make the decision that
> every time, everywhere, that you display the number,
> it must be like that, in every presentation system
> ever devised, in every conceivable usage?

Well, Percentages dont tend to vary very much,
they ALWAYS have % symbols appended all round
the world, right?
When it comes to the decimal place, this will come
down to the app we are writing for. If its a banking
one then more would be better, but usually nothing
more than 2 decimal places should suffice.

But all this is only for the ToString() function.

The Presentation Layer will never use the ToString()
function to display the value, would it? It would use the
value of the percentage directly.
How this value is displayed on the UI, is entirely up
to the UI which enforces the segregation between the
object and the UI logic.
(e.g.
writing a HTML.Format( ) which has an overloaded Percentages version, or changing the formatting of the label.
If the label is intelligent enough (using meta-info) to detect
that the datatype its being assigned to is a Percentage,
then it can get the regional settings for percentage
display and proceed to render it properly.)

With my beginner experience, heres a good requirement for our new datatype:
The Percentage datatype will hold a value of 1 to
signify 100% and should bankers round values to 2 decimal places (e.g. 67.89%).
so if vP = 0.445522; vp should equal 0.445500 (44.55%);

How will you implement this requirement if we were
to use plain doubles? write the setter function everytime
we have a percentage type property?
Or just make a new Percentage datatype and implement the rounding  there?
What if the requirement changed to 3 decimal places?
How many double properties would you have to track down?
Using your experience, would you severely regret using plain ol' doubles?


yk.

Yoon Kit Yong
Saturday, February 07, 2004

You use intermediary formatters, that are used to tie the two layers together. The formatters are chosen by the presentation layer, so they are appropriate for the given display and presentation system.

Brad Wilson (dotnetguy.techieswithcats.com)
Saturday, February 07, 2004

>The reason why I need to use my own datatype
>is because I want to apply Reflection / RTTI on my
>objects to do repetitive stuff.

What about using the standard double type and, instead of defining your own type, define your own attribute.  You can apply that attribute to properties of type double, in order to let your RTTI mechanism learn about their special characteristics....

John

John Rusk
Sunday, February 08, 2004

Hi John,

Ive thought about it (Jan 27th):

>>    :
>>  [DataTypeAttribute( dtPercentage )]
>>  public double TaxRate {}
>>    :

In fact I was going to fallback on this method
until Danko provided the solution.

I feel (v. subjective) that using attributes is abit unwieldly
for my programmers. Then we have
the developers confusion on how to treat this double.

So its both RTTI usage as well as development clarity.
Im still weighing my options:
1) stick to double with Percentage appended on each property
2) use double with Attributes denoting Percentage
3) make my own datatype Percentage
4) get over it, and just use double,
    and ignore confusion with devs, designers, analysts + ui guys.

Regards,

yk.

brad:> use intermediary formatters,
you mean like >> writing a HTML.Format( ) ?

Yoon Kit Yong
Sunday, February 08, 2004

*  Recent Topics

*  Fog Creek Home