Invoking Interface method using System.Object type variable in VB.NET
I have an interface ITest
with a method GetResult()
. I have a class Test which implements ITest and thereby defines private method GetResult().
Next I create an instance of Test in a different class. The code is as below:
Module NewClass
Public Sub New()
Dim i As ITest = New Test()
Dim o As Object
o = i
Dim b As Boolean = o.GetResult() 'This line raises "MissingMemberException" Public member 'Certify' on type 'Class1' not found. '
End Sub
End Module
I have to have Object o
referencing this as I have other code to follow开发者_StackOverflow which depends on o. Please help
Edit:
Here is the code in the calling module
Interface:
Public Interface ITest
Function GetResult() As Boolean
End Interface
Class Test
Implements ITest
Private Function ITest_GetResult() As Boolean Implements ITest.GetResult
Dim result As Boolean
......
......
Return result
End Function
End Class
Edit 2:
Thanks for the instant reply.
@Vlad - I am migrating existing code from VB 6 to VB.NET and hence I'm not supposed to change the access modifier of GetResult. Leaving it Private will throw InvalidCastException Unable to cast object of type 'System.Object' to type 'ITest'
@Dan - Object type variable o is used in many places and hence I don't want to change that. And yes, Test implements ITest.GetMember with a different name.
I have not work with VB.NET for a long time, but what you can try is:
Make GetResult() method as friend or public
If this doesn't work then try to cast your o object to ITest interface
(DirectCast(o, ITest)).GetResult()
But you still have to have your method that implements interface as friend or public.
I have to have Object o referencing this as I have other code to follow which depends on o.
What do you mean? Are you aware that anywhere you have code that expects an Object
, it can accept an ITest
? This is one of the big benefits of inheritance.
As for the MissingMemberException
: could you provide the definitions for ITest
, Test
, and Class1
? If the Test
type implements ITest.GetMember
using a method with a different name (this is legal in VB.NET), that could possibly explain the exception—though that doesn't appear to be precisely where the problem lies here. Seeing the relevant code from your Test
and Class1
classes in particular should help a good deal.
Edit: Based on your update, here's what's going on. You're mixing VB.NET's late binding feature with .NET's concept of interfaces. Both are supported in VB.NET, but they're not the same thing.
So when you have an Object
, you can call any method on it. This will be resolved at run-time. But it has to be a method that actually belongs to the object's type. In the case of Test
, there is no GetMember
function; there is only an ITest_GetMember
function. So to utilize late binding, you would need to call o.ITest_GetMember
(and this method would also need to be public, I believe).
That said, I strongly advise against mixing interface implementation and late binding in this way. In fact, I would recommend against late binding at all in cases like this where you actually have an interface. I really see no good reason to be casting your strongly-typed ITest
variable to one typed as Object
; as I stated before, you can always use an ITest
as an Object
(Object
is the root of the .NET type hierarchy; you can use anything as Object
), but what you can't always do is use an Object
as something else.
It just seems like you are sacrificing the usability of your i
variable (in the example code you posted) for no good reason.
Edit 2: Come to think of it, are you passing Object
references ByRef
? If so, I suppose that could be a legitimate reason (though I'm skeptical that you'd have good reasons for doing that).
If that's the case, you'll just need to cast your Object
back to an ITest
in order to use the members of ITest
. (See, since you already have an ITest
variable in i
, this is why I keep questioning your upcast to Object
.)
You can actually call the interface without having to typecast it IF you do not have Option Strict set to on, however, you will get the error you get with option strict set to on.
But then again, what is the correct way of doing this with Option Strict set to on?
EDIT I got the answer, which applies to my scenario and, to a larger extent, the question above. The only difference from my scenario is that the Interface is implemented in a satelite class (and therefore loaded as an assembly) So here goes the code solution that would apply to the question above IF the interface is implemented in an assembly that is loaded at runtime.
For Each x In AppDomain.CurrentDomain.GetAssemblies()
Dim myType = (From t In x.GetTypes() Where t.IsClass And t.GetInterface("ITest") IsNot Nothing Select t).FirstOrDefault
'you can add other criteria inside the linq expression above to match, e.g the assembly version and / or the assembly string (which contains assembly's version)
If myType IsNot Nothing Then
Dim obj = Assembly.GetAssembly(myType).CreateInstance(myType.FullName)
Dim result As Boolean = CType(myType.InvokeMember("ITest_GetResult", BindingFlags.Default Or BindingFlags.InvokeMethod, Nothing, obj, Nothing), Boolean)
End If
Next
This is a side effect of "boxing and unboxing" (or lack of unboxing). I can't remember any specific resource that I would recommend, but I would go read a few blogs that go in detail about the plumbing behind casting things to and back from the Object type. Basically since you're using late binding the VB ide/compiler will convert your code into code that uses reflection against that instance's type, which is Test. Refer to the code below for clarification:
Dim o As Object
o = i '<-- right here is "boxing". its "boxed" up and can be treated
''as an "Object" object. This allows it to be treated [covariantly][1]
'' What you typed:
Dim b As Boolean = o.GetResult() '<--right here it needs to be unboxed
''in order for the compiler to map the interface implementation. What
''happens instead is shown below.
'' What compiled: (after converted back to vb code from IL)
'' .....keep in mind, o.GetType() returns the same type as
''GetType(Test), NOT GetType(ITest)
Dim b As Boolean = o.GetType().InvokeMember("GetResult",
BidingFlags.Instance or BidningFlags.InvokeMethod
Or BindingFlags, Nothing, o, New Object() {})
Now the actually code probably is a little different, I'm just approximating it to demonstrate what's going on. Invoking a member like that from a Type object only searches and executes declared instance members. The runtime doesn't search and try to map interface implementations (for performance, security, and a number of other reasons). I'm not sure if you have any constraints on this, but you really need to go change all that code that uses the same variable. Every method call and property get/set is being routed through reflection.
精彩评论