I have come across something confusing or potentially a bug in Visual Studio 2010 when defining an interface in my VB application: When defining an interface method with a default parameter of type Double, using the Double.NaN constant as the default value causes the code editor/intellisense/precompiler some issues.
The following code underlines "INaNTest" and "INaNTest.DoSomething" claiming that 'DoSomething' cannot implement 'DoSomething' because there is no matching sub on interface 'INaNTest':
Public Class NaNTest
Implements INaNTest
Public Sub DoSomething(ByVal x As Double,
Optional ByVal a As Double = Double.NaN)
Implements INaNTest.DoSomething
End Sub
End Class
Public Interface INaNTest
Sub DoSomething(ByVal x As Double,
Optional ByVal a As Double = Double.NaN)
End Interface
Removing the implementation and starting from:
Public Class NaNTest
Implements INaNTest
End Class
Public Interface INaNTest
Sub DoSomething(ByVal x As Double,
Optional ByVal a As Double = Double.NaN)
End Interface
where now "NaNTest" is underlined (Class 'NaNTest' must ...), hitting the return key at the end of the line "Implements INaNTest" (i.e. automatically insert implementation) adds the implementation:
Public Sub DoSomething(ByVal x As Double,
Optional ByVal a As Double = -1.#IND)
Implements INaNTest.DoSomething
End Sub
in which the code editor then underlines '#' (Identifier expected.). Thus the code automatically added code that is not right.
Alternatively now, starting with the original code above, using the Error Correction Options button on the underlined "INaNTest.DoSomething" and selecting 'Generate method stub for 'DoSomething' in 'INaNTest', the added method stub is:
Sub DoSomething(ByVal x As Double,
Optional ByVal a As Double = NaN)
where now "NaN" has been divorced from the "Double." prefix and underlined ('NaN' is not declared. It may be inaccessible due to its protection level.) The code editor has inserted invalid code again.
Is there a correct solution to using Double.NaN as the default value for a method as defined on an interface, in VB.net, or is there a fundamental reason why this is impossible?
Many thanks,
JCollins
Ugh, that's fugly. Hard to characterize this as anything other than a bug. The default formatting for NaN when you let the IDE generate the method signature shows what language the VB.NET team uses, that's the way the C++ runtime library formats NaN. Attempts to convince it you know what you are doing are indeed futile, afaict.
You can report this at connect.microsoft.com. While you wait for the 'fixed in the next version of Visual Studio' to see the light of day, you might consider using nullable types as the workaround:
Public Class NaNTest
Implements INaNTest
Public Sub DoSomething(ByVal x As Double, Optional ByVal a As Double? = Nothing) Implements INaNTest.DoSomething
If a.HasValue Then
'' etc..
End If
End Sub
End Class
Public Interface INaNTest
Sub DoSomething(ByVal x As Double,
Optional ByVal a As Double? = Nothing)
End Interface
Fwiw, it does work when you use Double.Epsilon as the default value. Kinda silly but not an entirely unreasonable workaround either. Just don't let the IDE generate the implementation, then it gets silly.
Related
I would like to add the "Move" method as an extension method to the "List(Of...)".
I would like to add this to the generic list, not to a specific list.
My approach is this:
Imports System.Runtime.CompilerServices
Module ExtensionMethods
<Extension()>
Public Sub Move(ByRef uBase As List(Of T), ByVal index As Integer, ByVal newIndex As Integer)
Dim item As T = uBase.Item(index)
uBase.RemoveAt(index)
uBase.Insert(newIndex, item)
End Sub
End Module
The compiler doesn't accept the "T" in the lines "uBase As List(Of T)" and in " Dim item As T ="
What should be used here?
Thank you very much!
First, don't use ByRef on the target parameter. I'll expand on that later, because I want to skip to what will fix your compilation error.
Second, in order to have a type argument T in List(Of T), it has to exist in the method definition, so you need (Of T) on the method.
Imports System.Runtime.CompilerServices
Module ExtensionMethods
<Extension()>
Public Sub Move(Of T)(ByVal uBase As List(Of T), ByVal index As Integer, ByVal newIndex As Integer)
' ^^^^^^
Dim item As T = uBase.Item(index)
uBase.RemoveAt(index)
uBase.Insert(newIndex, item)
End Sub
End Module
Rule: An extension method should never** accept the target instance using ByRef.
**Exception to the rule: Certain value (Structure) types may need to be passed by reference to achieve reference type-like behavior (although value types should be immutable if at all possible) or to achieve better performance (in C#, you use the in keyword so the compiler prevents mutation of the instance).
Take this extension method, for example:
Module ExtensionMethods
<Extension()>
Public Sub ConfuseMe(Of T)(ByRef list as List(Of T))
list = New List(Of T)
End Sub
End Module
Dim myList As List(Of Integer)
Dim myList2 = myList ' A copy of the reference, but only one list object involved.
myList.Add(0)
myList.Add(1)
myList.Add(2)
myList.ConfuseMe() ' Call an extension method that can MODIFY myList
myList no longer points to the same instance. myList2 points to the original instance while myList points to the new one created in ConfuseMe. There's no reason the caller should expect that to happen.
So why would you ever do something like this? You probably wouldn't. But based on some of the comments and the confusion between references vs. references to references, I could see it accidentally happening. Using ByVal prevents it from ever becoming a difficult-to-track-down bug.
While it's possible in an extension method, you can't do that with a regular instance method.
Class TestClass
Sub ConfuseMe()
Me = New TestClass() ' Not possible on a Class
End Sub
EndClass
Dim x As New TestClass()
x.ConfuseMe() ' You wouldn't expect 'x' to refer to a different instance upon return
You can't do that. It won't allow you to assign to Me (again, value types are the exception), and you wouldn't expect x to point to a new instance after a call like this.
By the same token, it doesn't make sense to do it in an extension method, where the purpose is to behave like an instance method. And since you don't need to change the caller's variable, there's no need to take a reference to it. Just deal with the direct reference to the object instance by accepting it with ByVal.
I've seen some other responses about this and they talk about interfaces but I'm pretty sure you can do this with classes and base classes but I can't this to work.
Public Class Behavior
Private _name As String
Public ReadOnly Property Name As String
Get
Return _name
End Get
End Property
Public Property EditorUpdate As Boolean
Public Sub New(ByVal name As String)
_name = name
EditorUpdate = False
End Sub
Public Overridable Sub Update()
End Sub
' runs right away in editor mode. also runs when in stand alone game mode right away
Public Overridable Sub Start()
End Sub
' runs after game mode is done and right before back in editor mode
Public Overridable Sub Finish()
End Sub
' runs right when put into game mode
Public Overridable Sub Initialize()
End Sub
' runs when the game is complete in stand alone mode to clean up
Public Overridable Sub Destroy()
End Sub
End Class
Public Class CharacterController
Inherits Behavior.Behavior
Public Sub New()
MyBase.New("Character Controller")
End Sub
Public Overrides Sub Update()
' TODO: call UpdateController()
' THINK: how can UpdateController() get the controller entity it's attached to?
' Behaviors need a way to get the entity they are attached to. Have that set when it's assigned in the ctor?
End Sub
End Class
Dim plugins() As String
Dim asm As Assembly
plugins = Directory.GetFileSystemEntries(Path.Combine(Application.StartupPath, "Plugins"), "*.dll")
For i As Integer = 0 To plugins.Length - 1
asm = Assembly.LoadFrom(plugins(i))
For Each t As Type In asm.GetTypes
If t.IsPublic Then
If t.BaseType.Name = "Behavior" Then
behaviorTypes.Add(t.Name, t)
Dim b As Behavior.Behavior
b = CType(Activator.CreateInstance(t), Behavior.Behavior)
'Dim o As Object = Activator.CreateInstance(t)
End If
End If
Next
Next
When it tries to convert whatever Activator.CreateInstance(t) returns to the base class of type Behavior I'm getting invalid cast exception. That type should be of CharacterController which is defined as a child of Behavior so why wouldn't it let me cast that? I've done something like this before but I can't find my code. What am I missing?
This may not be an answer to your question (it also might resolve your exception -- who knows), but it is something that needs to be pointed out. These lines:
If t.IsPublic Then
If t.BaseType.Name = "Behavior" Then
Should really be changed to one conditional like this one:
If t.IsPublic AndAlso (Not t.IsAbstract) AndAlso _
GetType(Behavior.Behavior).IsAssignableFrom(t) Then
Otherwise, if somebody defines a random type called "Behavior" in their own assembly and derives it from another type, your code will think it is a plugin. Additionally, if someone derives your Behavior type and then derives that type (two levels of inheritance) this code will incorrectly skip over that type. Using the IsAssignableFrom method is a quick and easy way to ensure that one type does actually derive from the specific type you want (instead of any type that shares the same name), even if there is another type in between your types in the inheritance tree. The additional check against t.IsAbstract will also ensure that you don't try to instantiate an abstract subtype of your base plugin type.
This works for me:
Dim ctor As Reflection.ConstructorInfo = _
t.GetConstructor(New System.Type() {})
Dim o As Object = ctor.Invoke(New Object() {})
Dim plugin As Plugin = TryCast(o, Plugin)
(If I find t, I invoke the parameterless constructor.)
[I just realized this is probably what Activator.CreateInstance does, so I replaced my code with yours and it worked your way -- so this probably won't help you]
I have a BaseClass, a DerivedClass1 and a DerivedClass2 from a third party library. DerivedClass1 and DerivedClass2 both inherit from BaseClass.
There's a ContainerClass, from the same library, with a member variable ActiveItem, which can be of DerivedClass1 or DerivedClass2, so it is declared as BaseClass.
I want to know if ActiveItem is of DerivedClass1, as it can change in runtime without notice.
If I do
Dim isDerivedClass1 as boolean = TypeOf(oject.ActiveItem) Is DerivedClass1
then I get a compile time error, telling me that ActiveItem can never be of DerivedClass1 type.
I have tried several combinations of GetType and TypeOf but it doesn't seem possible to check this. I have also tried to declare an auxiliary DerivedClass1 variable and comparing their types, but haven't got any luck either.
Is there any workaround?
I guess I could do it with Reflection, but seems really overkill.
Edit:
The following code doesn't compile in vs2005 SP1.
Public Class Base
Public x As Integer
End Class
Public Class Derived1
Inherits Base
Public y As Integer
End Class
Public Class Derived2
Inherits Base
Public z As Integer
End Class
Public Class Unrelated
Public var As Base
End Class
Public Class Form1
Public Sub Test(ByVal obj As Unrelated)
Dim tst As Boolean
tst = TypeOf obj Is Derived1
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim obj As New Unrelated
obj.var = New Derived1
Test(obj)
End Sub
End Class
Edit:
It seems that the original problem was a fault in my side. I was checking against the wrong type (those silly third part libraries...)
However, I'm still trying to find the error in the code above.
Edit:
Again, my fault. I'm checking the Unrelated type against Base.
You code seems to be almost exactly right.
I've done this, which works fine:
Dim isDerivedClass1 As Boolean = TypeOf oject.ActiveItem Is DerivedClass1
Dim isDerivedClass2 As Boolean = TypeOf oject.ActiveItem Is DerivedClass2
Have I missed something?
EDIT: I think you just missed the var property in your edited code.
Public Sub Test(ByVal obj As Unrelated)
Dim tst As Boolean
tst = TypeOf obj.var Is Derived1
End Sub
You'll have to trust the compiler on this, it is convinced that DerivedClass1 does not inherit BaseClass. It doesn't get that wrong. That's either because it didn't see the Inherits clause in the DerivedClass1 declaration or because it picked a BaseClass definition from another assembly.
To fix the former problem, you have no alternative but to declare the ActiveItem as Object or to find another type that these classes have in common. Use the Object Browser. To fix the latter problem you'll have to change the Imports directive or spell out the full name of the BaseClass type (including namespace).
A simple possibility could be to use TryCast:
Dim isDerivedClass1 As Boolean = TryCast(object.ActiveItem, DerivedClass1) IsNot Nothing
Some predefined methods contain a ParamArray in their signature.
Delegates, however, cannot contain a ParamArray in their signature.
Question: Assume you wish to create a delegation mechanism for a specific method which requires a ParamArray. How would you work around this constraint?
EDIT: just to make clear, assume you cannot change the method signatures themselves (pre-defined methods, defined by some 3rd party, be it Microsoft or not).
EDIT2: The real deal here is keeping the syntax sugar, because the following code does work, but eliminates the sugar:
Public Delegate Sub MyDelegate(ByVal myArgs() As Object)
Public Sub PredefinedSub(ByVal ParamArray myArgs() As Object)
'...'
End Sub
Sub Test()
Dim aDelegate As New MyDelegate(AddressOf PredefinedSub)
aDelegate.Invoke(New Object() {1, 2, 3, 4})
End Sub
EDIT3: It turns out that Skeet's solutions is applicable also for creating Events and Operators containing a ParamArray.
Hmm... it works in C#:
using System;
class Test
{
delegate void Foo(params string[] args);
static void Main()
{
Foo f = x => Console.WriteLine(x.Length);
f("a", "b", "c");
}
}
However, you're right - the equivalent delegate declaration in VB fails:
Delegate Sub Foo(ParamArray ByVal args() As String)
Gives:
error BC33009: 'Delegate' parameters cannot be declared 'ParamArray'.
Curious. Fortunately, there's a way round it:
Imports System
Public Class Test
Delegate Sub Foo(<[ParamArray]()> ByVal args() As String)
Public Shared Sub Main()
Dim f As Foo = AddressOf PrintLength
f("a", "b", "c")
End Sub
Private Shared Sub PrintLength(ByVal x() As String)
Console.WriteLine(x.Length)
End Sub
End Class
Basically I've just applied ParamArrayAttribute manually. Seems to work fine.
However, none of this would have stopped you from using existing ParamArray methods anyway. Those methods are quite capable of taking normal arrays - so you could have declared your delegate types as normal and still created delegate instances which referred to those methods with no problems at all. The delegate type only affects how you would be able to call the delegate.
Other than declaring a delegate type with a parameter array, I don't really see what the issue was.
Are you sure that delegates do not support ParamArray? Ok, even if they don't, ParamArray is syntax sugar for plain old array. define parameter as array, that's it.
I am trying to implement a simple IEqulityComparer to use with LINQ collections. I have written the following code which is reduced to its simplest form for discussion purposes...
Public Structure bob
Dim SiteID As Integer
Dim fred As String
End Structure
Public Class insCompare
Implements System.Collections.Generic.IEqualityComparer(Of bob)
Public Function Equals(ByVal x As bob, ByVal y As bob) As Boolean
Return IIf(x.SiteID = y.SiteID, True, False)
End Function
Public Function GetHashCode(ByVal x As bob) As Integer
Return x.SiteID.GetHashCode()
End Function
End Class
The problem that I have is that both functions throw the compiler warning "function 'getHashCode' (or 'Equals') shadows an overridable method in the base class 'Object'. To override the base class method, this method must be declared 'Overrides'."
However, if I declare them as Overrides, I get the error "function 'GetHashCode' cannot be declared Overrides because it does not override a function in the base class."!!
I am also getting a compiler error on the "Implements" line to the effect that I must implement "getHashCode" but I presume that is a result of the first problem.
All my research indicates that I should be ok - anyone got any clues please?
This is a late answer to the question but as per the documentation you can do use the following. Notice the inclusion of the Overloads keyword.
Public Class MyModelComparer
Implements Generic.IEqualityComparer(Of MyModel)
Public Overloads Function Equals(x As MyModel, y As MyModel) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of MyModel).Equals
' do compare
End Function
Public Overloads Function GetHashCode(obj As MyModel) As Integer Implements System.Collections.Generic.IEqualityComparer(Of MyModel).GetHashCode
' do hashcode
End Function
End Class
Ok, it seems to get sorted by renaming the functions and declaring them as "Implements", although I have seen dozens of examples on the Web where this has not been the case.
However I now get a runtime exception which I will post separately.
Public Class insCompare
Implements System.Collections.Generic.IEqualityComparer(Of Object)
Public Function Equals1(ByVal x As Object, ByVal y As Object) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of Object).Equals
Return IIf(x.SiteID = y.SiteID, True, False)
End Function
Public Function GetHashCode1(ByVal x As Object) As Integer Implements System.Collections.Generic.IEqualityComparer(Of Object).GetHashCode
Return x.SiteID.ToString.ToLower.GetHashCode()
End Function
End Class
I am getting same problem. I am converting my C# code to VB.Net; Even Adding the Implements didn't help;
Using a shadow or overload removes all warning and errors. I wonder what is difference in behavior in both cases.
If I specify Overrides, I get an error.
Not specifying any of (overrides, overloads, shadows) gives a warning.
' <summary>
' Pair Comparator for maintaining uniquness in results.
' </summary>
Public Class PairComparer
Implements IEqualityComparer(Of Pair)
Public Shadows Function Equals(ByVal x As Pair, ByVal y As Pair) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of Pair).Equals
If x.first = y.first AndAlso x.second = y.second Then
Equals = True
ElseIf x.first = y.second AndAlso x.second = y.first Then
Equals = True
Else
Equals = False
End If
End Function
Public Overloads Function GetHashCode(ByVal obj As Pair) As Integer Implements System.Collections.Generic.IEqualityComparer(Of Pair).GetHashCode
GetHashCode = obj.first + obj.second
End Function
End Class
You're getting the compiler error because you are in VB.NET, not C#. In C#, having a method with the same signature as an interface method you need to implement makes the compiler wire it up for you automatically.
VB.NET requires that you explicitly implement say what method is being implemented. You can use the same name (it's encouraged), you just have to have that 'implements' clause.