Help with User-Defined Types

peterjp80

New member
Joined
Apr 2, 2007
Messages
3
Programming Experience
10+
Hi everyone! I'm usually pretty good at googling to find the information I need, but I am COMPLETELY stuck on this one! Any help would be greatly appreciated.

Here's what I'm trying to accomplish: I want a user-defined type (structure or class) for a phone number in either VB.NET, C++ or C# (preferably VB). I want it to work like most VB.NET types. For example:

Private x As String = "Test"
Private y As Phone = "6315551212"

How can I accomplish this? Any help would be greatly appreciated!
 
Define a CType operator, example:
VB.NET:
Public Class phone
 
    Public Shared Widening Operator CType(ByVal s As String) As phone
        Return New phone(s)
    End Operator
 
    Public Sub New(ByVal value As String)
        _input = value
    End Sub
 
    Private _input As String
 
    Public Property input() As String
        Get
            Return _input
        End Get
        Set(ByVal value As String)
            _input = value
        End Set
    End Property
End Class
 
Operator is used as a keyword to define the shared Ctype operator. It's not a method that you can call from the type template/instance (can't do phone.Ctype...). 'Public Shared' is required for operators. The operator can be Widening or Narrowing, and I choose Widening here since the type is guessed to expand (or not narrow) the original data, while if returned to String representation it is presumed to have no loss of original data. Look into documentation for how they describe this in the easier to understand Int32/Int64 example.

The usage requested was:
Private y As Phone = "6315551212"
This is implicit conversion that hides the true operation that happens. The explicit conversion would be:
Private y As Phone = Ctype("6315551212", Phone)
A Narrowing operator does not allow implicit conversion with Option Strict, but Widening does.
 
Private x As String = "Test"
Private y As Phone = "6315551212"

Scary.. writing one class type and enabling it to look like it is assignable from another, unrelated (from an inheritance hierarchy point of view) type

(I'd have written a Parse method, like Integer.Parse(), DateTime.Parse() etc..)
 
It's no different from any other type conversion like CInt, CDate etc and regular CType conversions.

Phone numbers are usually as a plain sequence numeric, but they are normally presented as a formatted string with spaces and other formatting characters that makes it easier to read and remember. So the relation from String type to Phone type is perfectly clear and near, this is also the case with other conversion/casting - there has to be a reason to use such code. But the operation is much more in need of parsing if it needs to be validated and perhaps stored as various numeric fractions (regional numbers) directly from that input. It may also be of matter where the string data comes from, user input or already validated data?
 
It's no different from any other type conversion like CInt, CDate etc and regular CType conversions.

Mmhh, yes but who writes:

Dim i As Integer = "123"
Dim d as Date = "31/12/2006"

Integer is rather simple, Date is more extensive, yet both have a Parse method. If we follow the notion that microsoft generally adopt best practices, it could be inferred that creating/using Parse for both simple and complex cases is a Good Idea :D

So the relation from String type to Phone type is perfectly clear
There is no inheritance hierarchy between Phone and String here, which was what I meant by "relation" - sure, there's an association in that a phone number can be represented by a String, but then, my name, an error message, the report title, a url - can all be represented by strings; but it doesnt warrant making a "UDT" from them and providing this conversion operator.. In the case of soemthing like URL, the only thus named class I could find was System.Security.Policy.Url, and a quick look revealed it to have neither a default property nor an operator overload to allow it to be assignable from a string, in code.

A quick look through the existing offerings, showed that it is only extensively used in database parameters, where for example, the specific System.Data.OracleClient.OracleString type has a CType to allow it to be assignable from a normal String. While I dont necessarily feel that this is great OO either, there is at least a more sensible corollorary. OracleString? String? Seems reasonable that they are both strings, and I understand the need for an equivalence to be established because the OracleTypes are essentially an interface to the typing system of another system entirely, where it doesnt necessarily make sense to descend the existing language types (where possible)

End of the day, its probably a personal programming style preference. I dont like Option Strict = off style of programming. To me a Phone is not a String, it is a black lump of plastic that sits on a desk and can be used to make voice calls to another human with a similar device.. Code that is broken in its typing consistency is something that scares me :)
 
cjard, would it make you happier if I named it PhoneNumber instead of Phone?

Having this phone "type" makes a lot of since to me. All of our phone database fields are consistently char(10). This type will have a parse function as well as format functions. It will accept string or long input. For example, the following code is all valid:

Dim phoneA As USPhoneNumber = "6315551212"
Dim phoneB As USPhoneNumber = 6315551212
Dim phoneC As USPhoneNumber = "(631) 555-1212"
Dim phoneD As USPhoneNumber = "1-631-555-1212"

This would throw an exeption:
Dim phoneE As USPhoneNumber = "hello"
Dim phoneF As USPhoneNumber = 631555 'not enough digits

If I did:
Dim phoneE As String = "hello"
that would be perfectly valid. So, instead of having validation and a seperate function to display the phone in the (###) ###-#### format, I now have the phone number strongly typed. When I put phoneA.ToString() into the database, I know I have a valid phone number. And when I use phoneA.ToFormatedPhoneNumber() I can have it return "(631) 555-1212"
 
cjard, an implicit widening CType conversion is a strict conversion if you care. And in any case a "String" is an abstraction. You don't scissor some written paper and call it a "string", but with some common sense most people understand that a "string" is some piece of text, while it really is just some paper and ink. Same with phone class I didn't get the impression that this class was to manifest itself as some hardware, but the abstraction "phone" meets the criteria for a class type that could receive, hold, and present a phone number in various formats with different properties. A "Person" class is not supposed to be born a real human (or represent ALL properties like skeleton blood etc), but is normally used with programming and not uncommonly have a "phone" property.

I don't see the difference between these two examples when the string is supposed to have a meaning:
VB.NET:
Dim P As New Person("John")
Dim P As Person = "John"

These 3 expression produces the same value, the first two does the same thing and is equal in functionality as the above 'phone' example. The usage depends on the input and its possible validation. I'm sure you have noticed it is very common with programming and in some cases inevitable to convert between "string" and the numeric value it represent.
VB.NET:
Dim i As Integer = CInt("12")
Dim i As Integer = CType("12", Integer)
Dim i As Integer = Integer.Parse("12")
I would say "string" is as abstract in programming as "byte". Nothing in the real world is a byte, but we still handle all kinds of data as bytes.

And don't phone me about this, I gather your carrier would hurt ;)
 
cjard, would it make you happier if I named it PhoneNumber instead of Phone?

Probably more like "PhoneNumberString"

Having this phone "type" makes a lot of since to me. All of our phone database fields are consistently char(10). This type will have a parse function as well as format functions. It will accept string or long input. For example, the following code is all valid:

Dim phoneA As USPhoneNumber = "6315551212"
Dim phoneB As USPhoneNumber = 6315551212
Dim phoneC As USPhoneNumber = "(631) 555-1212"
Dim phoneD As USPhoneNumber = "1-631-555-1212"

OK, so we can say that your phone number is analogous to Microsoft's DateTime, because there may be many different formats, and (as JohnH mentioned) a phone number has several components.. Maybe an area code, non-geographical identifer, country code, extension. A date have many components, hours, minutes, seconds etc..

What I'm saying is, Microsoft didnt provide a CType operator on date; you have to Parse or ParseExact it..

Accordingly, if I were making a class to handle phone numbers, I would follow a similar pattern, and my PhoneNumber would have a Parse (and maybe a ParseExact if I was really looking to drive myself mental)

In my code, you wouldnt see any of these:
Dim phoneA As USPhoneNumber = "6315551212"
Dim phoneB As USPhoneNumber = 6315551212
Dim phoneC As USPhoneNumber = "(631) 555-1212"
Dim phoneD As USPhoneNumber = "1-631-555-1212"

You'd have:
Dim phoneA As USPhoneNumber = USPhoneNumber.Parse("6315551212")
Dim phoneB As USPhoneNumber = USPhoneNumber.Parse(6315551212)
Dim phoneC As USPhoneNumber = USPhoneNumber.Parse("(631) 555-1212")
Dim phoneD As USPhoneNumber = USPhoneNumber.Parse("1-631-555-1212")

Of course, in the end, Im sure the same methods get called, but it's about how the code documents itself. I'm an Option Strict = On programmer. I dont expect to see unrelated object types being assigned to each other.

I'm not saying what you want to do is wrong; it just makes me go "Ugh" because (IMHO) it looks sloppy/like you dont know or care what Types are

If I did:
Dim phoneE As String = "hello"
that would be perfectly valid. So, instead of having validation and a seperate function to display the phone in the (###) ###-#### format, I now have the phone number strongly typed. When I put phoneA.ToString() into the database, I know I have a valid phone number. And when I use phoneA.ToFormatedPhoneNumber() I can have it return "(631) 555-1212"
This makes perfect sense to me, and indeed, it's not the bit I dont think is good; your argument for representing something as complex as a phone number in its own type is highly commendable.. Its just realising it by direct assignment from a String that looks wrong..

I wasnt overjoyed by VB6's default properties for a similar reason, and I guess Microsoft follow that idea too, because default properties can now only be found on things that embody some kind of collection.

You now can no longer say:
Textbox1 = "Hello World"

You must say:
Textbox1.Text = "Hello world"


However:
Dim myStrings as New List<string>
myStrings(0) = "hello"
myStrings.Item(1) = "Hello"

are equivalent.

This looks okay; because they are collections and the syntax is familiarly sensible from array..
 
I don't see the difference between these two examples when the string is supposed to have a meaning:
VB.NET:
Dim P As New Person("John")
Dim P As Person = "John"

Whereas I (personally) really dont think the latter looks good, sensible or self documenting at all..

I would say "string" is as abstract in programming as "byte". Nothing in the real world is a byte, but we still handle all kinds of data as bytes.

Me too! An established convention in code in most languages is:

"This is a string, because it is demarked by quotes"


Other demarkers exist to a greater or lesser defacto standard. Microsoft Access SQL might use dates like:

SELECT ... WHERE dateCol = #1/1/1901#

The # being used to demark the dates, and again, if the type on the left hand side of the assignment is date, the type on the right hand side is also date, (through association tha # are date demarkers), then type equivalence is satisfied.

Turning this debate on its head for a moment...

If:

Dim P As Person = "John"
Dim pho As Phone = "1-800-123-4567"

Is acceptable/sensible, why is the following disallowed with Option Explicit = On:

Dim i as Integer = "123"

i.e. why did MS not write a widening (not-narrowing) CType op that allows this conversion?


Of course, I'm thus advocating that Microsoft are the paragon of correctness, which is an opinion not everyone may share.. I'd say they are right because they are consistent, with themselves and with other industry languages, and they appear to ahve put a lot of thought into everything .NET does (across all its syntaxes)
 
Operator is used as a keyword to define the shared Ctype operator. It's not a method that you can call from the type template/instance (can't do phone.Ctype...). 'Public Shared' is required for operators. The operator can be Widening or Narrowing, and I choose Widening here since the type is guessed to expand (or not narrow) the original data, while if returned to String representation it is presumed to have no loss of original data. Look into documentation for how they describe this in the easier to understand Int32/Int64 example.

The usage requested was:
Private y As Phone = "6315551212"
This is implicit conversion that hides the true operation that happens. The explicit conversion would be:
Private y As Phone = Ctype("6315551212", Phone)
A Narrowing operator does not allow implicit conversion with Option Strict, but Widening does.

gotcha, i'd never seen this which of course made me curious
 
Dim i as Integer = "123"

i.e. why did MS not write a widening (not-narrowing) CType op that allows this conversion?
There is no way of knowing if string input will not narrow when cast to other type, usually it does because the exact same string value will not be returned by a conversion from a type to String.

To turn the question around, why did they allow the Widening as implicit in strict mode? It because here is a contract to provide to original data unchanged when converting back to the original type. This is the definition of Widening (no data loss through conversion). In this case the original string must be provided when converting from the custom type to String, if not the rule is broken.
 
To turn the question around, why did they allow the Widening as implicit in strict mode? It because here is a contract to provide to original data unchanged when converting back to the original type. This is the definition of Widening (no data loss through conversion).

I dont know whether the contract is necessarily round-trip.. "Why can int be implicitly converted to string with Option Strict = On?" It cant.. Not on my system at least. Maybe I misunderstood your post?
 

Attachments

  • Image1.png
    Image1.png
    4.3 KB · Views: 21
Back
Top