Why would VS keep saying to add this?
Sub New()
' TODO: Complete member initialization
End Sub
Since you have a constructor that takes parameters, a default, parameterless constructor is not created for you automatically.
Your LINQ statement is not calling your parameterized constructor, it need a parameterless constructor.
Basically, when you code this:
Select New CommunityEvent With {.Day = sRecord(0), etc. }
What gets generated is this
Dim obj As New CommunityEvent() 'Uses parameterless constructor
obj.Day = sRecord(0)
'etc.
When you remove the CommunityEvent With part, then it doesn't use your class, it creates an anonymous type instead.
Related
I'm trying to be able to pass a Type parameter to a function called ConvertList, and have that function create some instances of the specified type. So, if I passed in type Foo, the function would create new objects of type Foo and put the created objects into a custom List object (SLMR_OBjList).
The function is in a generic class that is defined:
Public Class BOIS_Collection_Base(Of T)
The function would accept types other than what is passed in the class definition. So, if we create an instance of BOIS_Collection_Base(Of MyTypeA) we may call the function ConvertList(Of MyTypeB).
I want the private variable _convertedList to be of a different type than the class. Is this possible? I can only seem to define it with (Of T).
Here is what I have so far:
Public Class BOIS_Collection_Base(Of T)
Private _convertedList As SLMR_ObjList(Of T) ' I can only seem to define this as (Of T), but want to make sure I can pass in a Type other than the Base(Of T)
Public Function ConvertedObjList(Of myT)() As SLMR_ObjList(Of T) ' Should this be (Of T) or (Of myT) since I want it to use whatever Type is passed in
For Each tempVar In Me.ObjList
Dim newitem As myT = Activator.CreateInstance(GetType(myT), tempVar)
' Next line won't compile, says on newitem 'Value of type 'myT' cannot be converted to 'T'
_convertedList.Add(newitem)
Next
_convertedList.Sort_Direction = Me.Sort_Direction
_convertedList.Sort_Expression_List = Me.Sort_Expression_List
Return _convertedList
End Function
Here is what I would like to be able to do:
Dim mainCollInstance As New BOIS_Collection_Base(Of MyTypeA)
....
'Code that populates the BOIS_Collection_Base.ObjList property with an SLMR_ObjList(Of MyTypeA)
....
' Now I want to take that ObjList, and cast all the items in it to MyTypeB
Dim newListObj As SLMR_ObjList(Of MyTypeB) = mainCollInstance.ConvertList(Of MyTypeB)
Is this possible? Am I going about it wrong?
In response to Plutonix:
If I define _convertedList inside the method, like this:
Public Function ConvertedObjList(Of myT)() As SLMR_ObjList(Of myT)
Dim _convertedList = New SLMR_ObjList(Of myT)
my errors go away, and the method does what I want, but _convertedList is no longer persistant in the object.
If you want to persist the list, then you can't really allow the consuming code to pass a different type for the list each time. That doesn't really make much sense, unless each time it's called, you only want the function to return the portion of the persisted list which contains objects of the given type. If that's the case, then you just need to declare _convertedList As SLMR_ObjList(Of Object) and then filter it and convert it to the correct type as necessary.
If, however, as I suspect is the case, the consumer will always be requesting that it be converted to the same type each time the function is called, then that output type is not really a property of the function call. Rather, it's a property of the whole class. In that case, you should make your class take two generic type arguments, like this:
Public Class BOIS_Collection_Base(Of T, TOut)
Private _convertedList As SLMR_ObjList(Of TOut)
Public Function ConvertedObjList() As SLMR_ObjList(Of TOut)
For Each tempVar As T In Me.ObjList
Dim newitem As TOut = DirectCast(Activator.CreateInstance(GetType(TOut), tempVar), TOut)
' Next line won't compile, says on newitem 'Value of type 'myT' cannot be converted to 'T'
_convertedList.Add(newitem)
Next
_convertedList.Sort_Direction = Me.Sort_Direction
_convertedList.Sort_Expression_List = Me.Sort_Expression_List
Return _convertedList
End Function
End Class
Based on the previous related question and an assumption that MyTypeA and MyTypeB inherit from the same class (never got an answer), you may not need Generics for this. At any rate, this should help with the ctor part of the question. I do not as yet see where Generics fit in since inheritance may do what you want already:
Class MustInherit BiosItem
Public Property Name As String
Public Property TypeCode As String
...
MustOverride Function Foo(args...) As Type
Overridable Property FooBar As String
' etc - the more stuff in the base class the better
End Class
Class TypeA
Inherits ABClass
Public Sub New
MyBase.New ' stuff common to all child types
TypeCode = "A" ' EZ type ID rather than GetType
...
End Sub
End Class
Class TypeB would be the same, but initialize TypeCode to "B". The same for C-Z. These allow you to poll the object rather than needing GetType: If thisObj.TypeCode = "A" Then.... Now, the collection class:
Public Class BIOSItems
Inherits Collection(Of BiosItem)
' inheriting from Collection<T> provides Add, Count, IndexOf for us
' most important the Items collection
'
End Class
Typing the collection as BiosItem will allow TypeA or TypeJ or TypeQ in it. As is, your collection will hold one Type only as it should be. This works because an item which is GetType(TypeA) is also GetType(BiosItem). See also note at the end.
Converting one item to another would seem to be something that would largely be handled by the NEW item being created or converted to. Since they are likely to be very similar then it can be handled by a constructor overload (if they are not similar, well we are well down the wrong road):
' a ctor overload to create the new thing based on the old things props
Public Sub New(oldThing As BiosItem)
MyClass.New ' start with basics like TypeCode, MyBase.New
With BiosItem ' coversion
myFoo = .Foo
myBar = .Bar ' copy common prop vals to self
...
Select Case .TypeCode
Case "B"
myProp1 = .Prop33 ' conversions
myProp3 = .Prop16 + 3.14
...
End Select
' then initialize stuff unique to this type maybe
' based on other props
If .PropX = "FooBar" Then myPropZ = "Ziggy"
End With
End Sub
Code to create, convert, store:
Dim varOldBItem As TypeB = myBiosCol(ndx) ' get old item
Dim varAItem As New TypeA(varOldBItem) ' call the ctor above
myBiosCol.Add(varAItem) ' add new item
myBiosCol.Remove(varoldBItem) ' delete the old if need be
If BOIS_Collection_Base is always supposed to contain MyTypeA, then type it that way (inheriting from Collection<T> still seems in order). If also MyTypeB objects are never added to the collection directly, but converted to MyTypeA first (Edit makes that less clear), then most of the above still applies, except for the inheritance. A ctor overload on MyTypeA could still take an old B object and create itself based on it. I'd be less inclined to do it via the ctor if they do not inherit from the same base class, but it could be done.
I am using Rhino Mocks along with nUnit to attempt to test my function IsApprovable() on an object. The function I am testing relies on another object "UserManager" that needs to be passed in. I am attempting to mock an instance of UserManager so I can specify the result of another function GetApproverDependantsList()
My issue is that when I mock the object and pass it into the function I am testing I get the following InvalidCastException:
Unable to cast object of type 'Castle.Proxies.IUserManagerProxye15b431a53ca4190b7ffbdf5e241e2bb' to type 'MyNamespace.Users.UserManager'.
I am new to this so I am not really sure if I am doing things correctly... Here is the sample mocking code I am using:
Dim helper As New BookingManagerHelper()
Dim booking As Booking = GetDummyBooking() 'method to get a booking in suitable state
Dim parameters As Collection(Of Parameter) = GetDummyParameters() 'factory method, as above
Dim mockedUserManager = MockRepository.GenerateMock(Of Users.IUserManager)()
'I have created a dummy function called GetUserCollectionWithDependant() to create the results I need the mocked function to return...
mockedUserManager.Stub(Function(x) x.GetApproverDependantsList(-1)).[Return](GetUserCollectionWithDependant(1))
'It's the line below where I find my exception...
Assert.AreEqual(True, helper.IsApprovable(booking, mockedUserManager, parameters))
The function I am attempting to test looks like the following:
Public Function IsApprovable(ByVal Booking As Booking , ByVal UserManager As Users.UserManager, Optional ByVal Parameters As Collection(Of Parameter) = Nothing) As Boolean
'various logic checks are performed on the objects passed in
End Function
Some things to note:
UserManager implements the interface IUserManager
UserManager contains additional properties and functions that are not defined by the interface
UserManager also inherits from a base class (do I need to override base properties?)
I can post more code if needed. Thanks in advance.
I am no expert in VB.NET but IsApprovable expects a UserManager instance. The mock is of type IUserManager. Maybe you want to adjust IsApprovable to use a IUserManager instance.
I want to format any numeric type using a method call like so:
Option Infer On
Option Strict Off
Imports System.Runtime.CompilerServices
Namespace GPR
Module GPRExtensions
<Extension()>
Public Function ToGPRFormattedString(value) As String
' Use VB's dynamic dispatch to assume that value is numeric
Dim d As Double = CDbl(value)
Dim s = d.ToString("N3")
Dim dynamicValue = value.ToString("N3")
Return dynamicValue
End Function
End Module
End Namespace
Now, from various discussions around the web (VB.Net equivalent for C# 'dynamic' with Option Strict On, Dynamic Keyword equivalent in VB.Net?), I would think that this code would work when passed a numeric type (double, Decimal, int, etc). It doesn't, as you can see in the screenshot:
I can explicitly convert the argument to a double and then .ToString("N3") works, but just calling it on the supposedly-dynamic value argument fails.
However, I can do it in C# with the following code (using LINQPad). (Note, the compiler won't let you use a dynamic parameter in an extension method, so maybe that is part of the problem.)
void Main()
{
Console.WriteLine (1.ToGPRFormattedString());
}
internal static class GPRExtensions
{
public static string ToGPRFormattedString(this object o)
{
// Use VB's dynamic dispatch to assume that value is numeric
var value = o as dynamic;
double d = Convert.ToDouble(value);
var s = d.ToString("N3").Dump("double tostring");
var dynamicValue = value.ToString("N3");
return dynamicValue;
}
}
So what gives? Is there a way in VB to call a method dynamically on an argument to a function without using reflection?
To explicitly answer "Is there a way in VB to call a method dynamically on an argument to a function without using reflection?":
EDIT: I've now reviewed some IL disassembly (via LinqPad) and compared it to the code of CallByName (via Reflector) and using CallByName uses the same amount of Reflection as normal, Option Strict Off late binding.
So, the complete answer is: You can do this with Option Strict Off for all Object references, except where the method you're trying exists on Object itself, where you can use CallByName to get the same effect (and, in fact, that doesn't need Option Strict Off).
Dim dynamicValue = CallByName(value, "ToString", CallType.Method, "N3")
NB This is not actually the equivalent to the late binding call, which must cater for the possibility that the "method" is actually a(n indexed) property, so it actually calls the equivalent of:
Dim dynamicValue = CallByName(value, "ToString", CallType.Get, "N3")
for other methods, like Double.CompareTo.
Details
Your problem here is that Object.ToString() exists and so your code is not attempting any dynamic dispatch, but rather an array index lookup on the default String.Chars property of the String result from that value.ToString() call.
You can confirm this is what is happening at compile time by trying value.ToString(1,2), which you would prefer to attempt a runtime lookup for a two parameter ToString, but in fact fails with
Too many arguments to 'Public ReadOnly Default Property Chars(index As Integer) As Char'
at compile time.
You can similarly confirm all other non-Shared Object methods are called directly with callvirt, relying upon Overrides where available, not dynamic dispatch with calls to the Microsoft.VisualBasic.CompilerServices.NewLateBinding namespace, if you review the compiled code in IL.
You are using a lot of implicit typing, and the compiler doesn't appear to be assigning the type System.Dynamic to the variables you want to be dynamic.
You could try something like:
Option Infer On
Option Strict Off
Imports System.Runtime.CompilerServices
Namespace GPR
Module GPRExtensions
<Extension()>
Public Function ToGPRFormattedString(value as System.Dynamic) As String
' Use VB's dynamic dispatch to assume that value is numeric
Dim d As Double = CDbl(value)
Dim s = d.ToString("N3")
Dim dynamicValue as System.Dynamic = value.ToString("N3")
Return dynamicValue
End Function
End Module
End Namespace
Option Infer On
Option Strict Off
Imports System.Runtime.CompilerServices
Namespace GPR
Module GPRExtensions
<Extension()>
Public Function ToGPRFormattedString(value) As String
Dim dynamicValue = FormatNumber(CDbl(value), 3)
Return dynamicValue
End Function
End Module
End Namespace
suppose I declare a class like this:
Class tst
Public Props As New Dictionary(Of String, MyProp)
End Class
and added properties something along these lines:
Dim t As New tst
t.Props.Add("Source", new MyProp(3))
but now want to access it like this:
t.Source
how can I create a getter without knowing the name of the getter?
Ok, if you insist on "auto-vivifying", the only way I know of to do something like that is to generate the code as a string, and then compile it at runtime using the classes in the System.CodeDom.Compiler namespace. I've only ever used it to generate complete classes from scratch, so I don't know if you could even get it to work for what need to add properties to an already existing class, but perhaps you could if you compiled extension methods at runtime.
The .NET framework includes multiple implementations of the CodeDomeProvider class, one for each language. You will most likely be interested in the Microsoft.VisualBasic.VBCodeProvider class.
First, you'll need to create a CompilerParameters object. You'll want to fill its ReferencedAssemblies collection property with a list of all the libraries your generated code will need to reference. Set the GenerateExecutable property to False. Set GenerateInMemory to True.
Next, you'll need to create a string with the source code you want to compile. Then, call CompileAssemblyFromSource, passing it the CompilerParameters object and the string of source code.
The CompileAssemblyFromSource method will return a CompilerResults object. The Errors collection contains a list of compile errors, if there are any, and the CompiledAssembly property will be a reference to your compiled library (as an Assembly object). To create an instance of your dynamically compiled class, call the CompiledAssembly.CreateInstance method.
If you're just generating a small amount of code, it's pretty quick to compile it. But if it's a lot of code, you may notice an impact on performance.
Here's a simple example of how to generate a dynamic class containing a single dynamic property:
Option Strict Off
Imports System.CodeDom.Compiler
Imports Microsoft.VisualBasic
Imports System.Text
Public Class Form3
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim code As StringBuilder = New StringBuilder()
code.AppendLine("Namespace MyDynamicNamespace")
code.AppendLine(" Public Class MyDynamicClass")
code.AppendLine(" Public ReadOnly Property WelcomeMessage() As String")
code.AppendLine(" Get")
code.AppendLine(" Return ""Hello World""")
code.AppendLine(" End Get")
code.AppendLine(" End Property")
code.AppendLine(" End Class")
code.AppendLine("End Namespace")
Dim myDynamicObject As Object = generateObject(code.ToString(), "MyDynamicNamespace.MyDynamicClass")
MessageBox.Show(myDynamicObject.WelcomeMessage)
End Sub
Private Function generateObject(ByVal code As String, ByVal typeName As String) As Object
Dim parameters As CompilerParameters = New CompilerParameters()
parameters.ReferencedAssemblies.Add("System.dll")
parameters.GenerateInMemory = True
parameters.GenerateExecutable = False
Dim provider As VBCodeProvider = New VBCodeProvider()
Dim results As CompilerResults = provider.CompileAssemblyFromSource(parameters, code)
If results.Errors.HasErrors Then
Throw New Exception("Failed to compile dynamic class")
End If
Return results.CompiledAssembly.CreateInstance(typeName)
End Function
End Class
Note, I never use Option Strict Off, but for the sake of simplicity in this example, I turned it off so I could simply call myDynamicObject.WelcomeMessage without writing all the reflection code myself.
Calling methods on objects using reflection can be painful and dangerous. Therefore, it can be helpful to provide a base class or interface in a shared assembly which is referenced by both the generated assembly, and the fixed assembly which calls the generated assembly. That way, you can use the dynamically generated objects through a strongly typed interface.
I figured based on your question that you were just more used to dynamic languages like JavaScript, so you were just thinking of a solution using the wrong mindset, not that you really needed to or even should be doing it this way. But, it is definitely useful in some situations to know how to do this in .NET. It's definitely not something you want to be doing on a regular basis, but, if you need to support custom scripts to perform complex validation or data transformations, something like this can be very useful.
I have a List(Of AddlInfo) with AddlInfo being an object.
I'm trying to pass addlInfoList by reference into a function of another class:
Public Shared Sub SortAddlInfo(ByRef addlInfoList As List(Of AddlInfo))
addlInfoList.Sort(AddressOf Comparer)
End Sub
Private Function Comparer(ByVal x As AddlInfo, ByVal y As AddlInfo) As Integer
Dim result As Integer = x.AddlInfoType.CompareTo(y.AddlInfoType)
Return result
End Function
This works if I'm not passing the reference into another class, but when I try to do this, I get the following error:
Overload resolution failed because no accessible 'Sort' can be called with these arguments:
'Public Sub Sort(comparison As System.Comparison(Of AddlInfo))': Cannot refer to an instance member of a class from within a shared method or shared member initializer without an explicit instance of the class.
'Public Sub Sort(comparer As System.Collections.Generic.IComparer(Of AddlInfo))': 'AddressOf' expression cannot be converted to 'System.Collections.Generic.IComparer(Of MyProject.AddlInfo)' because 'System.Collections.Generic.IComparer(Of MyProject.AddlInfo)' is not a delegate type.
I could put the methods back into the calling class, but I'd like to be able to call these methods from different classes within my application.
I could also instantiate a fresh List in the methods, but why? Seems silly.
Any way around this? (Or do I need to explain more?)
Thanks in advance!
Try putting your compare function into a class that implements IComparer.