VB.NET: Operator '=' is not defined ... for a variable and object of THE SAME TYPE? - vb.net

Okay, I am TOTALLY confused here. I have a class... say MyClass. It has several properties of another class of my type, say MyHelperClass (along with other properties).
I am doing the following:
Dim inst As MyClass = New MyClass() With {
.p1 = sv1,
.p2 = sv2,
.h1 = getHelperClass(a1),
.p3 = sv3,
.p4 = sv4,
.h2 = getHelperClass(a2),
.p5 = sv5,
...
.pN = svN
}
*where .p# is some property, .sv# is some valid value. .h# is a property of type MyHelperClass and getHelperClass(a#) returns an instance of said class.
Now, I have the odd thing here, where the assignment statement for h1 works perfect. No problems. The assignment statement for h2 however, it is giving me the following blue-squiggle error:
Operator '=' is not defined for types myLib.MyHelperClass and myLib.MyHelperClass.
I just do not get this error at all! I don't even know where to start to figure this out. HELP!
201105.06 0305:
The signature for h1's type is List(Of myLib.Address), where Address is a very basic class with typical address fields (name, address, city, state, zip, etc.). The return type of getHelperClass is also List(Of myLib.Address).
As SSS hinted at in his answer, I would expect = to not work in the "natural" way if I was using it for equality testing on a class without operators, however I am using it as an assignment operator, not equality, which I can't see any problem with. I am expecting the result of getHelperClass to be assigned to h2. But instead it's telling me = is not defined for the type. Is it possible that for some reason, the compiler is interpreting it as =(EQUALS) instead of =(ASSIGN)?
As for commenting out that line and it happening on the first one, I'll need to wait till I'm back in the office tomorrow to check that. Will report back.

Ah, yeah, sorry didn't read your OP properly. The assignment must be being misinterpreted as an comparison. Maybe you are missing a comma? For example in the statement "a = b = c" the first equals sign is an assignment, the second is a comparison.

You need to add the Operator methods to MyHelperClass
e.g.
Public Shared Operator =(byval a as MyHelperClass, byval b as MyHelperClass) As Boolean
...
End Operator
Public Shared Operator <>(byval a as MyHelperClass, byval b as MyHelperClass) As Boolean
...
End Operator
You should also read up on the difference between Reference and Value types.

Related

Why Are My Tables Returning NIL and Not Populating?

I'm new to Lua and trying to understand the concept of OOP in Lua. To do so, I've tried creating an object and creating methods and "private variables". My issue is when I try to use "setters" or "getters", it's indicating that my tables are returning NIL which means I'm either having a scoping issue or something else I can't figure out.
The kicker is I'm using an example from an online Lua coding tutorial, and when I run the tutorial it works flawlessly. However, when I run mine, I get NIL or nothing outputs whenever I try to "get" or return a value from one of the member functions.
I'm using a couple of different environments:
ZeroBrain
Sublime Text
Lua for Windows
Do you know why my code is not returning populated tables?
newPlayer = function(n, h, a, r)
player = {}
n = n or ""
h = h or 100
a = a or 100
r = r or 0
function player:getPlayerName()
return n
end
function player:getPlayerHealth()
return h
end
function player:getPlayerArmor()
return a
end
function player:getPlayerRank()
return r
end
function player:setPlayerName(arg)
n = arg
end
function player:setPlayerHealth(arg)
h = arg
end
function player:setPlayerArmor(arg)
a = arg
end
function player:setPlayerRank(arg)
r = arg
end
function player:connect(arg)
print(string.format(" %s joined" , arg))
end
return player
end
player1 = newPlayer("John", 100, 100, 1000)
player1.getPlayerName()
Your code does not contain "populated tables" to return.
Your newPlayer function does create a table, and it does return it. It creates a number of functions within that table. But that's all newPlayer does: creates a table and puts some functions in it.
The data accessed by those functions is not part of the table. n, h, a, and r (BTW, please use better variable names) are all local variables. Your inner functions will access the specific stack containing those variables, but the variables themselves will not be magically associated with the table.
Your principle problem is almost certainly with the setters. And it comes from a combination of this:
function player:setPlayerName(arg)
with this:
player1.getPlayerName()
When you create a function using a : character between a table name and the function's name, you are using syntactic sugar for a function which implicitly takes as its first argument a value called self. As the name suggests, this is supposed to represent the object which this function is being called upon. So your function creation code is equivalent to:
function player.setPlayerName(self, arg)
Since you create all of your functions with :, all of your functions take at least one parameter.
The : syntax can also be used when calling such functions. If you did player1:getPlayerName(), this would cause the table you accessed to find the getPlayerName function to be used as the first argument in the function call. So that line would be equivalent to player1.getPlayerName(player1).
Obviously, these two syntaxes are mirrors of one another: functions created with : take a parameter that is expected to refer to the table it is being called on, and functions called with : will be given the table which was accessed to get that function.
But... your code didn't stick to the symmetry. You created the functions with :, but you call them with .
Now, you get functions are able to get away with this because... well, none of your values are actually part of the table. So your get functions just return the local value that they adopted from their creating context.
The set functions pose a problem. See, they take a parameter. But because the function was declared with :, they really take two parameters, the first being the implicit self.
Now, : syntax is just syntactic sugar; it's just a convenient way to do what you could have done yourself. So it is in theory OK to call a function with . even if you created it with :. But if you do so, you must pass the table as the first parameter. Though your code doesn't show it, I strongly suspect you didn't do that.
If you called player1.setPlayerName("foo"), what will happen is that the implicit self parameter will get the value "foo", and the arg parameter will be nil. And you will assign that nil value to the n local variable. So subsequent calls to player1.getPlayerName() will return nil.
Basically, what's going on here is that you're combining two different ways of creating objects in Lua. You stored your private data in a way that external code cannot access (ie: local upvalues), but that data is now no longer part of the table itself. Which means that, although you dutifully create those functions with : syntax to indicate that they take a self table, they never actually use that table. And because they never use the table, it's a lot harder to figure out what's going wrong.
Basically, the key here is to be symmetrical. If you create a function with :, then you should either call it with : or make sure to pass it the object table as the first parameter.
Broadly speaking, the standard way to create private members is by convention, not by forbidding it. That is, you agree not to mess with any members of a table other than those with certain names. Python convention is to pretend that names starting with _ don't exist, and Lua programs sometimes use that.
Upvalues are an interesting solution for private variables, but they do come with problems. If you want to invent a member variable, you have to do it in a centralized place rather than wherever you might need one. Even if the variable is optional, you have to create a named local at the top of the function.
TLDR of Nicol's answer, see my answer to another question:
function player:setPlayerArmor(arg)
a = arg
end
The : syntax is syntactic sugar. It creates an implicit 'self' argument when declared, and when used. If you declare it one way and use it another, the arguments won't be what you're expecting. Say your player has 100 health. Look at this result:
player1.setPlayerHealth(55, 66)
print(player1.getPlayerHealth())
-- will display '66', not '55' because `:` declares implicit 'self' argument
This displays 66 because the setPlayerHealth function has an implicit 'self' parameter because it was declared with :. If you instead called it
with the ::
player1:setPlayerHealth(55, 66)
print(player1:getPlayerHealth())
-- will display '55' because `:` passes player1 as self
function player:setHealth1(arg)
-- implicit 'self' argument refers to player1 when called on player1
end
-- is the same as
function player.setHealth2(self, arg)
-- with `.` notation, you need to add the 'self' argument explicitly
end
player1.setHealth1(31) -- self argument will be 31 and arg will be nil
player1.setHealth2(32) -- self argument will be 32 and arg will be nil
player1:setHealth1(33) -- self argument will be player1 and arg will be 33
player1:setHealth2(34) -- self argument will be player1 and arg will be 34

String.Concat with non-string arguments, unexpected behaviour

As a summary: I'm trying to get String.Concat to use a reference type's ToString overload when sticking string together.
Edit: Added this overview: The example code below is a cooked down extract of my real code - as such it would be immediately obvious when refactoring if I only had two lines of code. The important issue here (to me) is that I changed from a string to an object and there was no compile error from String.Concat. Equally it's behaviour wasn't what I would have expected (Using my object's to string method, rather than the bog standard object name). If I'd been using "&", there would have been a compile error. I'm concerned that the String.Concat syntactic sugar may lead to bugs that otherwise would have been avoided (in this case when refactoring). I'd like to know if there's a way of altering the behaviour of String.Concat or if I should consider it to be dangerous.
The situation:I've got a solution which processes a whole heap of data; I was using a String to contain the identifier of each piece of data, but have just swapped this out for a class (FeatureIdentifier) to enable me to extend the identifier to include things like batches etc.
I've refactored my code so that I use this class instead of just the string. When refactoring this type of thing (rightly or wrongly) I tend to rely on compile errors as a to-do list.
Now, I'm a self-taught programmer and I'm probably a bit set in my ways (I tend to look at new features in terms of if they let me do anything new rather than if they let me do stuff I can already do only easier) and I've just come across something which makes me sad.
So, I was sticking my identifier onto an underscore onto a type. My code looked like this:
Dim x as string = "MyIdentifer"
dim myOutputValue as string = String.Concat(x,"_ANCHOR.txt")
Running this I got myOutputValue equal to "MyIdentifier_ANCHOR.txt". Following refactoring, my code looked like this:
Dim x as new FeatureIdentifier("MyIdentifier")
dim myOutputValue as string = String.Concat(x,"_ANCHOR.txt")
Running this I got myOutputValue equal to "MyNamespace.FeatureIdentifier_ANCHOR.txt".
Having kicked myself and implemented a ToString method on my class, I run it again and get exactly the same output (that is "MyNamespace.FeatureIdentifier_ANCHOR.txt"). In immediate, if I do: ?x.ToString, I get "MyIdentifier", so I'm certain I've implemented ToString correctly.
So, here's my problem. I like the syntax of String.Concat but I don't like the fact that it doesn't do one of:
a) calling ToString on reference types it sticks together orb) throwing a compile error if you pass it non-string based arguments. The old school: x & "_ANCHOR.txt" gives me a compile error (which I would have picked up when refactoring).
Here's what I've tried:
I've tried shadowing the String.Concat function with an extension (something like this:
<System.Runtime.CompilerServices.Extension()> Public Function Concat(...some arguments...) As String
Return String.Concat(...some arguments...)
End Function
) but hit two problems:
1) When trying to narrow the type of the arguments down to string to cause compile errors, I realised that the argument is a param array and hence objects in the first place. So fail there.
2) When I tried to make multiple overloads ((s1 as string, s2 as string), (s1 as string, s2 as string, s3 as string) etc), I felt it was a bit lame and also discovered that you can't actually overload an extension on a static class (which is what I guess String is).
So, does anyone know a way of getting String.Concat to behave as well as old-school concatenation, or should I avoid String.Concat in favour of old-school concatenation?.
(I'm not going to use a StringBuilder, as I'm only concatenating a few strings and I don't believe this is the place for one).
I don't believe there is any way that you are going to "fix" the String.Concat method to only allow strings. Chalk it up to another reason why shared methods should be created and used as sparingly as possible. However, through the miracle of operator overloading, you can make your custom class work just like a string. To fix the Concat method, you need to overload the CType operator. To fix the string concatenation operator (&), you need to overload that operator separately, like this:
Public Class FeatureIdentifier
Public Sub New(id As String)
Me.Id = id
End Sub
Public Property Id As String
Public Property SomethingElse As Integer
Public Overloads Shared Widening Operator CType(value As FeatureIdentifier) As String
Return value.Id
End Operator
Public Overloads Shared Operator &(value1 As FeatureIdentifier, value2 As String) As String
Return value1.Id & value2
End Operator
Public Overloads Shared Operator &(value1 As String, value2 As FeatureIdentifier) As String
Return value1 & value2.Id
End Operator
End Class
Now you can use it like this:
Dim x As New FeatureIdentifier("MyIdentifier")
Dim myOutputValue As String = String.Concat(x, "_ANCHOR.TXT")
Or like this:
Dim x As New FeatureIdentifier("MyIdentifier")
Dim myOutputValue As String = x & "_ANCHOR.TXT"
And it will work just like as if it were still a string, in those circumstances. You may also want to overload some of the other operators too, just in case. For instance, the + operator also concatenates when applied to two strings. However, I should caution you that operator overloading can cause confusion to people who are not familiar with the code, since it works unexpectedly, so you should only use it if it really makes sense to do so.
Do you need to override ToString and even make sure it's called for this? It would seem to be more proper to expose a property of FeatureIdentifier named Identifier, or some such thing, and then you can just do:
Dim myOutputValue as string = String.Concat(x.Identifier, "_ANCHOR.txt")

Exclamation operator?

I'm learning D and have seen a lot of code like this:
ushort x = to!ushort(args[1]);
I assume this casts args[1] to ushort, but what's the difference between this and cast(ushort)?
EDIT: And what other uses does the exclamation mark operator have?
In D,
to!ushort(args[1])
is shorthand for the template instantiation
to!(ushort)(args[1])
and is similar to
to<ushort>(args[1])
in languages like C++/Java/C#.
The exclamation point is to note the fact that it's not a regular argument, but a template argument.
The notation does not use angle brackets because those are ridiculously difficult to parse correctly for a compiler (they make the grammar very context-sensitive), which makes it that much more difficult to implement a correct compiler. See here for more info.
The only other use I know about is just the unary 'not' operation (e.g. false == !true)... I can't think of any other uses at the moment.
Regarding the cast:
cast(ushort) is an unchecked cast, so it won't throw an exception if the value is out of range.
to!ushort() is a checked cast, so it throws an exception if the value is out of range.
The exclamation mark here is not an operator, it is just a token part of the explicit template instantiation syntax (described in detail here).
std.conv.to (docs) is a function template for converting between arbitrary types. It is implemented entirely in the library and has no special support in the language. It has a broader and different scope compared to the cast operator.
The to template takes two type parameters; a "to" type and a "from" type, in that order. In your example, the template is explicitly instantiated with the single type argument ushort for the "to" parameter, and a second type argument string (assuming args comes from the first parameter to main) is automatically inferred from the regular function argument passed to the function (args[1]) as the "from" parameter.
The resulting function takes a string parameter and returns a ushort parsed from that string, or throws an exception if it failed. The cast operator will not attempt this kind of high-level conversion.
Note that if there is more than one explicit template parameter, or that parameter has more than one token in it (ushort is a single keyword token), you must wrap the template parameter list in parentheses:
ushort result;
result = to!(typeof(result))(args[1]);
In this example, typeof, (, result and ) are four separate tokens and the parentheses are thus required.
To answer your last question, the ! token is also used for the unary not operator, unrelated to template instantiations:
bool yes = true;
bool no = !yes; // 'no' is false
You already got two excellent answers by jA_cOp and Merhdad. I just want answer directly to the OP question (what's the difference between this and cast(ushort)?) - The difference is that cast(ushort)args[1] will not work (you cannot cast from a string to an uint just like that), while the to!(type)(param) template knows what to do with the string and how to convert it to the primitive type.

Using List.Exists and Predicates correctly

All
I am currently trying implement something along the lines of
dim l_stuff as List(of Stuff)
dim m_stuff as new Stuff
m_stuff.property1 = 1
m_stuff.property2 = "This"
if not l_stuff.exists(m_stuff) then
l_stuff.add(m_stuff)
end if
This fails obviously as the Exist method is looking for a predicate of Stuff.
Can anyone fully explain the predicate and how i can achieve what I am trying to do here.
I have tried to use
if not l_stuff.contains(m_stuff) then
l_stuff.add(m_stuff)
end if
however this doesn't detect the idenitcal entry and enters a duplicate into the list
Thank
List(Of T).Contains is the method you should be using. Exists, as you say, expects a predicate. Of course, for .Contains to work as expected, you need to override the Equals() method, as well as GetHashCode().
List(Of T).Exists expects a function that will return a Boolean value when passed an item of type T, where T, in your case, is of type Stuff. So, you could write a method that looks like:
If Not l_stuff.Exists(Function(x) x.property1 = m_stuff.property1 And _
x.property2 = m_stuff.property2) Then
and so on.

VB.NET Get underlying system.type from nullable type

I'm attempting to create a dataset based on the properties of an object. For example, I have an instance of a Person class with properties including ID, Forename, Surname, DOB etc. Using reflection, I'm adding columns to a new dataset based on the object properties:
For Each pi As PropertyInfo In person.GetType().GetProperties()
Dim column As New DataColumn(pi.Name, pi.PropertyType)
table.Columns.Add(column)
Next
My problem is that some of those properies are nullable types which aren't supported by datasets. Is there any way to extract the underlying system type from a nullable type?
Thanks.
Here's your answer, in VB. This may be overkill for your purposes, but it also might be useful to some other folks.
First off, here's the code to find out if you're dealing with a Nullable type:
Private Function IsNullableType(ByVal myType As Type) As Boolean
Return (myType.IsGenericType) AndAlso (myType.GetGenericTypeDefinition() Is GetType(Nullable(Of )))
End Function
Note the unusual syntax in the GetType. It's necessary. Just doing GetType(Nullable) as one of the commentors suggested did not work for me.
So, armed with that, you can do something like this... Here, in an ORM tool, I am trying to get values into a generic type that may or not be Nullable:
If (Not value Is Nothing) AndAlso IsNullableType(GetType(T)) Then
Dim UnderlyingType As Type = Nullable.GetUnderlyingType(GetType(T))
Me.InnerValue = Convert.ChangeType(value, UnderlyingType)
Else
Me.InnerValue = value
End If
Note that I check for Nothing in the first line because Convert.ChangeType will choke on it... You may not have that problem, but my situation is extremely open-ended.
Hopefully if I didn't answer your question directly, you can cannibalize this and get you where you need to go - but I just implemented this moments ago, and my tests are all passing.
Nullable.GetUnderylingType(myType)
will return the underlying type or null if it's not a nullable type.
I'm guessing that the problem is recognizing whether the property is nullable or not. In C# you do this with this code:
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
... but I'm not sure what the equivalent of that last clause is in VB.NET.
You can also use the GetGenericParameters() method on that type. myNullableObject.GetType().GetGenericParameters()[0] should give you the type of nullable it is (so Guid, Int32, etc.)
#Mendelt Siebenga: You can only call GetType on the value property if the variable is not set to null; otherwise, you'll get an exception.
What you want to do is use the "GetValueOrDefault" property and call GetType on that, since you are guaranteed it will not be null. Example:
Dim i As Nullable(Of Integer) = Nothing
Dim t As Type = i.GetValueOrDefault().GetType()