VB.NET unable to differentiate between overloaded functions - vb.net

Current version of MVVM Light has a helper function named Set in ObservableObject class that an inheriting ViewModel class can call to both change property value and raise change notification in one call. Together with the new NameOf operator, this makes the boilerplate code of properties much smaller.
The problem however is that the Set function is overloaded and out of the 3 overloads, the following 2 overloads make VB.NET angry:
Protected Function [Set](Of T)(propertyName As String, ByRef field As T, newValue As T) As Boolean
Protected Function [Set](Of T)(ByRef field As T, newValue As T, <CallerMemberName> Optional propertyName As String = Nothing) As Boolean
Now if you have a String type property, VB.NET cannot differentiate as to which overload we are calling.
Overload resolution failed because no accessible '[Set]' is most specific for these arguments:
'Protected Overloads Function [Set](Of String)(propertyName As String, ByRef field As String, newValue As String) As Boolean': Not most specific.
'Protected Overloads Function [Set](Of String)(ByRef field As String, newValue As String, [propertyName As String = Nothing]) As Boolean': Not most specific.
Note that C# can handle this situation easily, by using ref keyword. Also that even though the current situation is related to MVVM Light, the problem itself is generic. I have tried to use named parameters too, but that doesn't help either. Any hints on how this could be solved?

Here again after almost a year. I just found a little workaround that would work in most cases. Instead of calling one of the overloads mentioned in the question, use the third overload:
Protected Function [Set](Of T)(ByRef field As T, newValue As T, <CallerMemberName> Optional propertyName As String = Nothing) As Boolean
The third parameter of this overload is optional and if you skip it in the call, it will use CallerMemberName to assign it a value. Since Set is almost always called from within the property, this approach should work nicely. No other overload takes two parameters, so compiler can resolve it correctly.

Related

How can I disambiguate '=' symbol in VB.NET, in a lambda function

I am using Dapper to query a flat list of items from a database, into a POCO class as follows:
Public Class Node
Public Property Name As String
Public Property ParentNodeName As String
Public Property Children As IEnumerable(Of Node)
End Class
I am trying to use the accepted answer to this question, in order to create a tree out of the flat list.
The only caveat is that I am using VB.NET.
I have tried it a straightforward port of the C# solution:
nodes.ForEach(Function(n) n.Children = nodes.Where(Function(ch) ch.ParentNodeName = n.Name).ToList)
but it does not compile with the error
Error BC30452 Operator '=' is not defined for types 'List(Of Node)' and 'List(Of Node)'.
The = symbol is interpreted as an equality operator, while I meant to use the assignment operator.
I have pasted the C# code into the telerik converter, and the converted code is:
Private Shared Function BuildTree(ByVal items As List(Of Category)) As IEnumerable(Of Category)
items.ForEach(Function(i) CSharpImpl.__Assign(i.Categories, items.Where(Function(ch) ch.ParentId = i.Id).ToList()))
Return items.Where(Function(i) i.ParentId Is Nothing).ToList()
End Function
Private Class CSharpImpl
<Obsolete("Please refactor calling code to use normal Visual Basic assignment")>
Shared Function __Assign(Of T)(ByRef target As T, value As T) As T
target = value
Return value
End Function
End Class
It uses an helper class to solve this issue, but suggests a refactor to avoid this.
Hence the questions:
Is there a general way to disambiguate equality = and assignment = in VB.NET, without resorting to an helper class and a specific function to assignement
In this specific case, is there a simple refactor I can use to get rid of the issue?
That's because of VB.Net distinction between functions and subroutines.
Instead of
nodes.ForEach(Function(n) n.Children = nodes.Where(Function(ch) ch.ParentNodeName = n.Name).ToList)
use
nodes.ForEach(Sub(n) n.Children = nodes.Where(Function(ch) ch.ParentNodeName = n.Name).ToList)
When you use Function, the lambda expression is expected to return a value; and in your case it looks like it wants to return a boolean.
But you want to use a lambda expression that does not return anything (in your case, you want an assignment), you have to use Sub.

Val Function and overload notice

in the MSDN, this is the description of VAL() function:
This member is overloaded
What's overload property? why part of functions or methods in .NET have this property?
From MSDN:
In an OOP language such as Microsoft® Visual Basic® .NET, you are allowed to create methods in a class that have the same name but different argument lists. Visual Basic .NET can figure out which method to call during compile based on the parameter types that you pass. This technique is called overloading a method.
Example for VAL()
Public Overloads Function Val(ByVal InputStr As String) As Double
' -or-
Public Overloads Function Val(ByVal Expression As Object) As Double
' -or-
Public Overloads Function Val(ByVal Expression As Char) As Integer
Function overloading allows you to have 2 functions with the same name but with different arguments.
Eg:
DoSomething();
DoSomething(int arg1);

VB.NET Using System.Threading.Volatile.Write on Enum var types

I am in need of performing a volatile write on a variable that is an Enum type derived from Byte, but I am stucked.
This is my (example) code:
Public Class MyOwnClass
Friend Enum MyEnum As Byte
Val1
Val2
End Enum
Private MyEnumVar As MyEnum = MyEnum.Val1
Friend Sub SetMyEnumVar(ByVal value As MyEnum)
System.Threading.Volatile.Write(MyEnumVar, value) 'Error!
End Sub
End Class
Since Threading.Volatile.Write is not provided with a signature with those arguments I get this error
Error 1 Overload resolution failed because no accessible Write can be called without a narrowing conversion:
With the list of all the overloads of the method.
CTyping the first argument is not working, because CType returns a casted value of course not with the same reference as MyEnumVar where the method gets the first parameter abviously ByRef instead.
CObject that would return a reference is also not viable because the method also hasn't got the overload for an object type other than Write(Of T)(T, T) where Tmust be a class type.
So how can I accomplish my purpose?
Thanks.
You can use the Write(Of T) overload where T is the type of Enum.
System.Threading.Volatile.Write(Of [Enum])(MyEnumVar, value)

Why doesn't Entities-to-Linq in VB.NET like Lambda when Option Strict is ON?

Updated with new code:
I've got some very simple Linq using Entities
Return NDCEntity.tbl_Ext_Mobile_PO.Where(Function(p) If(p.AssignedToUID, 0) = UserUID)
I can do that in C# without trouble by simple replacing Function(p) with p =>. But in VB.NET there's an added problem. If I try doing the above with Option Strict ON, then i get this massive compiler error.
I tried the solution suggested over at Entity Framework Where method parameters and changed my code to have the If(p.AssignedToUID, 0) but that gives me a very similar error.
Which is apparently some kind of casting error. I've seen several variations of people asking about this on this site and others, but none of the ones I found actually answer it: what do you have to do to make this work with Option Strict ON? I much prefer to work with it on.
Update:
Here's the error text:
Error 1 Overload resolution failed because no accessible 'Where' can be called with these arguments:
'Public Function Where(predicate As String, ParamArray parameters() As System.Data.Objects.ObjectParameter) As System.Data.Objects.ObjectQuery(Of tbl_Ext_Mobile_PO)': Lambda expression cannot be converted to 'String' because 'String' is not a delegate type.
Extension method 'Public Function Where(predicate As System.Func(Of tbl_Ext_Mobile_PO, Integer, Boolean)) As System.Collections.Generic.IEnumerable(Of tbl_Ext_Mobile_PO)' defined in 'System.Linq.Enumerable': Nested function does not have a signature that is compatible with delegate 'System.Func(Of tbl_Ext_Mobile_PO, Integer, Boolean)'.
Extension method 'Public Function Where(predicate As System.Func(Of tbl_Ext_Mobile_PO, Boolean)) As System.Collections.Generic.IEnumerable(Of tbl_Ext_Mobile_PO)' defined in 'System.Linq.Enumerable': Cannot infer a common type, and Option Strict On does not allow 'Object' to be assumed.
Extension method 'Public Function Where(predicate As System.Linq.Expressions.Expression(Of System.Func(Of tbl_Ext_Mobile_PO, Integer, Boolean))) As System.Linq.IQueryable(Of tbl_Ext_Mobile_PO)' defined in 'System.Linq.Queryable': Nested function does not have a signature that is compatible with delegate 'System.Func(Of tbl_Ext_Mobile_PO, Integer, Boolean)'.
Extension method 'Public Function Where(predicate As System.Linq.Expressions.Expression(Of System.Func(Of tbl_Ext_Mobile_PO, Boolean))) As System.Linq.IQueryable(Of tbl_Ext_Mobile_PO)' defined in 'System.Linq.Queryable': Cannot infer a common type, and Option Strict On does not allow 'Object' to be assumed. C:\Programming\Sources\NDC\Custom Web Services\Mobile SAP WebServices\1.0.0.0\MobileSAPWebServices\MobileSAPWebServices\SAPMobileWS.asmx.vb 29 20 MobileSAPWebServices
Should I be DirectCasting this to something?
See my edit.
Try this:
Return NDCEntity.tbl_Ext_Mobile_PO.Where(Function(p) If(p.AssignedToUID.HasValue, p.AssignedToUID.Value, 0) = UserUID)
think the issue might be that VB can't correctly infer that p is always going to be an Integer and instead guesses that it is nullable. You can also try DirectCast, Convert.ToInt32, or CInt like so:
Return NDCEntity.tbl_Ext_Mobile_PO.Where(Function(p) DirectCast(If(p.AssignedToUID, 0), Integer) = UserUID)
EDIT: You are comparing Object to Guid, since p can evaluate to either a Guid or 0. Change the zero to a Guid and you should be good to go. Try this:
Return NDCEntity.tbl_Ext_Mobile_PO.Where(Function(p) If(p.AssignedToUID, Guid.Empty) = UserUID)
OR
Return NDCEntity.tbl_Ext_Mobile_PO.Where(Function(p) If(p.AssignedToUID, Nothing) = UserUID)

VB.Net - how to support implicit type conversion as well as custom equality

Fixed: See notes at bottom
I am implementing a generic class that supports two features, implicit type conversion and custom equality operators. Well, it supports IN-equality as well, if it does that.
1) if ( "value" = myInstance ) then ...
2) Dim s As String = myInstance
3) Dim s As String = CType(myInstance,String)
The problem I am having is that if I support #2, implicit conversion, then I can't get my equality operators to work, since they complain about no conversion being the most specific.
The error I get is this (simplified a bit for brevity):
Overload resolution failed because no accessible '=' is most specific for these arguments:
'Public Shared Operator =(obj As MyClass, data As String) As Boolean': Not most specific.
'Public Shared Operator =(data As String, obj As MyClass) As Boolean': Not most specific.
'Public Shared Operator =(obj1 As MyClass, obj2 As MyClass) As Boolean': Not most specific.
What is the best way of implementing this. Just as importantly, what should I leave out? I have implemented the following conversions
Operator =(ByVal data As String, ByVal obj As classType) As Boolean (and <>)
Operator =(ByVal obj As classType, byval data As String) As Boolean (and <>)
Operator =(ByVal obj1 As classType, ByVal obj2 As classType) As Boolean (and <>)
Equals(obj as Object) as Boolean
Equals(compareTo as classType ) as Boolean
Equals(compareTo as String) as Boolean
Widening Operator CType(ByVal source As String) As classType
Widening Operator CType(ByVal source As classType) as String
Narrowing Operator CType(ByVal inst As classType) As dataType
In my widening operator I do some reflection, which is why I wanted to be able to do an implicit convert DOWN to String when I do a comparison or assignment with the string on the left side.
A) SomeObject.StringPropertySetter = MyClass
Fix (edit)
I went way overboard in what I implemented, because I didn't understand what was happening. Comparison between the base types (ie string/double/guid) takes place via the widening ctype(...) as String (or Guid,etc) operator. In the end, i just implemented these functions and all my test cases still pass, in addition to assignment from the class to a base type instance
Public Class MyClass(Of BaseType)
Widening Operator CType(ByVal source As dataType) As MyClass
Widening Operator CType(ByVal source As MyClass) As dataType //conv between inst & base
Equals() // for datatype, classType, object
Operator <>(MyClass,MyClass) // for comparison between two instances
Opeator =(MyClass,MyClass)
comments are c style, but code is vb.net
Of course the class is a little more complicated than that, but that give me everything I needed :)
You should not override the = operator. If you have implicit conversions to types such as string or int, then let the default equality operator take over.
As a general rule, if you need to customize equality for a class you should override the Equals(object) method.