Overloads New Method With Args - vb.net

I've been build a large application that requires the need to overload the NEW method with args (Common I guess).
I have lots of properties in some classes and most if not all get overloaded in the new method. I wondered if there is a way to capture the arguments from the method and assign the values to the custom object by looking through the properties/args. All my property name within the class match the ones being passed as args in the method so matching the "name" properties should be possible???
OR a less taxing way of manually typing me.property = arg for each and every argument...
for example:
public class myClass
property arg1 as string
property arg2 as string
property arg3 as string
public sub new(arg1 as string, arg2 as string, arg3 as string)
For each arg in methodbase.getcurrentmethod.getParameters
custObj(<property name>).value = arg.value where custObj.name = arg.name
end for
end sub
end class
This isn't valid VB.net syntax is just an example of what I'm trying to achieve.

There is no real solution to your problem because reflection can not get a parameters value. But:
You can hack your way around this by creating an anonymous type inside
your method and taking advantage of projection initialisers. You can
then interrogate the anonymous type's properties using reflection.
https://stackoverflow.com/a/1868507/4035472
The result could be something like this:
Class [MyClass]
Public Property arg1 As String
Public Property arg2 As String
Public Property arg3 As String
Public Sub New(arg1 As String, arg2 As String, arg3 As String)
Dim hack = New With { arg1, arg2, arg3}
Dim all = BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic
Dim props = GetType([MyClass]).GetProperties(all).ToDictionary(Function(x) x.Name, Function(x) x)
For Each p As PropertyInfo In hack.GetType().GetProperties()
props(p.Name).SetValue(Me, p.GetValue(hack))
Next
End Sub
End Class

Related

.Where method not defined on generic typed list?

When I try to use the .Where() method on a list, this does method does not seem to be defined if the list is of a generic type:
In my program, I have a class called Warning, and in another class, a list of warnings, defined as:
Dim warningList As List(Of Warning)
When I try to manipulate this list as:
Dim item = warningList.Where(Function(x) x.GetName() = "Foo").FirstOrDefault()
This works completely fine, but when I try it like this:
Dim itemList
if(type = "Warning") Then 'Please note that this condition is true...
itemList = warningList
End If
Dim item = itemList.Where(Function(x) x.GetName() = "Foo").FirstOrDefault()
I get an exception, stating that method .Where() is not defined for class Warning
Can anybody tell me why this is?
Thank you!
Now that you've edited your question it's clear.
You declare itemList without a type, so it's Object implicitly(in VB.NET with option strict set to Off which i strongly recommend against).
Now that you have declared a variable of type Object you can asssign any type to it. But you would have to cast it back to its real type List(Of Warning) to be able to use list or LINQ methods(which extend IEnumerable(Of T).
But instead declare it with the correct type:
Dim itemList As List(Of Warning)
if(type = "Warning") Then
itemList = warningList
End If
Dim item = itemList.Where(Function(x) x.GetName() = "Foo").FirstOrDefault()
Including to comment to explain why Warning is not related to this problem:
That's not the real code. If warningList is really a List(Of Warning)
you should be able to use Enumerable.Where(if LINQ is
imported). The fact that you assign this instance to another variable
(on declaration) doesn't change anything because that variable's type
is also a List(Of Warning). So itemList.Where should work too. Warning
has nothing to do with it because the type which is extended by Where
is IEnumerable(Of T), T can be any type(even Object). Since List(Of T)
implements IEnumerable(Of T) you can use Enumerable.Where on any list
(or array).
If you actually have multiple types and Warning is just one of it, you should implement a common interface. Here's an example:
Public Enum NotificationType
Warning
Info
[Error]
End Enum
Public Interface INamedNotification
ReadOnly Property Type As NotificationType
Property Name As string
End Interface
Public Class Warning
Implements INamedNotification
Public Sub New( name As String )
Me.Name = name
End Sub
Public Property Name As String Implements INamedNotification.Name
Public ReadOnly Property Type As NotificationType Implements INamedNotification.Type
Get
Return NotificationType.Warning
End Get
End Property
End Class
Now you can declare a List(Of INamedNotification) and fill it with whatever implements this interface, like the Warning class:
Dim notificationList As List(Of INamedNotification)
if type = "Warning" Then
itemList = warningList
Else If type = "Info"
itemList = infoList
End If
Dim item = notificationList.Where(Function(x) x.Name = "Foo").FirstOrDefault()

Convert a Variable To a Propery Variable Using Reflection

I am trying to convert
Public Class TestClass
Public FirstName As String
End Class
To
Public Class AnotherClass
Public Property FirstName As String
End Class
I wrote a function that will convert the member of one class to member of another class, so if I pass some class type that has Public Property LastName AS String it will convert it to (for instance) AnotherClass Type variable and I will be able to get the value so I am happy here.
Public Shared Function ConvertModelToValidationDataModel(Of T)(ByVal oSourceObject As Object) As T
Dim oSourceObjectType As Type
Dim oSourceObjectProperties() As PropertyInfo
Dim oDestinationObjectProperties() As PropertyInfo
Dim oDestinationObject As Object
Dim oDestinationObjectType As Type
oDestinationObject = Activator.CreateInstance(Of T)()
oDestinationObjectType = GetType(T)
oDestinationObjectProperties = oDestinationObjectType.GetProperties
oSourceObjectType = oSourceObject.GetType()
oSourceObjectProperties = oSourceObjectType.GetProperties()
If Not oSourceObjectProperties Is Nothing Then
If oSourceObjectProperties.Count > 0 Then
For Each oDestinationObjectPropertyInfo As PropertyInfo In oDestinationObjectProperties
For Each oSourceObjectPropertyInfo As PropertyInfo In oSourceObjectProperties
If oDestinationObjectPropertyInfo.Name = oSourceObjectPropertyInfo.Name Then
oDestinationObjectPropertyInfo.SetValue(oDestinationObject, oSourceObjectPropertyInfo.GetValue(oSourceObject, Nothing))
End If
Next
Next
End If
End If
Return oDestinationObject
End Function
The problem is I want to pass TestClass (the variable FirstName is not a property but I want it to be converted to a property variable) and be able to convert it and get the value but for some reason it does not pass the value and obviously it looks like the function converts it to a non-property variable of another class - not the property variable like I want it to.
**
Short version:
**
When I pass in a class type that has property variables (Public Property FirstName As String) - I get back a class of another type, all the values are passed and converted to property variables.
When I pass in class type that has variables (Public FirstName As String) I am not able to get the value and it does not convert it to property variable.
Question: Why I am not able to get the value and convert it to a property variable when passing in a class type that has a non-property variable?
Solution
Thanks the guys in the comment section below for helping me visualize the fact that I was asking an object for the properties while object only had fields.
Here is the Updated Version of the function for those who interested
Public Shared Function ConvertModelToValidationDataModel(Of T)(ByVal oSourceObject As Object) As T
Dim oSourceObjectType As Type
Dim oSourceObjectFields() As FieldInfo
Dim oDestinationObjectProperties() As PropertyInfo
Dim oDestinationObject As Object
Dim oDestinationObjectType As Type
oSourceObjectType = oSourceObject.GetType()
oSourceObjectFields = oSourceObjectType.GetFields()
oDestinationObject = Activator.CreateInstance(Of T)()
oDestinationObjectType = GetType(T)
oDestinationObjectProperties = oDestinationObjectType.GetProperties
If Not oSourceObjectFields Is Nothing Then
If oSourceObjectFields.Count > 0 Then
For Each oSourceObjectFieldInfo As FieldInfo In oSourceObjectFields
For Each oDestinationObjectPropertyInfo As PropertyInfo In oDestinationObjectProperties
If oSourceObjectFieldInfo.Name = oDestinationObjectPropertyInfo.Name Then
oDestinationObjectPropertyInfo.SetValue(oDestinationObject, oSourceObjectFieldInfo.GetValue(oSourceObject))
End If
Next
Next
End If
End If
Return oDestinationObject
End Function

.net - Using Class as one parameter

I have a class with several properties.
Public Class test
Public Property a As String
Public Property b As String
Public Property c As String
Public Property d As String
Public Property e As String
Public Property f As String
Public Property g As String
End Class
In my VB.net code, I am assigning a value to each property.
I want to send the whole test class as one parameter, and use all the values inside it.
So that if I add extra parameters later on, I want them to be used dynamically, instead of writing this everytime:
Textbox1.text= test.a & test.b & test.c .......
Any way to do this?
Im not really writing the values in a textbox, but this is just an simplified example.
I think what you want is a property. You'll need to add a property to your class like:
Public Property Combination() As String
Get
Return a & b & c & d & e ...
End Get
End Property
Then to get the value you'd use
Textbox1.text = test.combination
(for more details you can see http://www.dotnetperls.com/property-vbnet)
I recommend you override the built-in ToString function. Also, to further simplify this, add a CType operator.
Public Class test
Public Property a As String
Public Property b As String
Public Property c As String
Public Property d As String
Public Property e As String
Public Property f As String
Public Property g As String
Public Shared Widening Operator CType(obj As test) As String
Return If((obj Is Nothing), Nothing, obj.ToString())
End Operator
Public Overrides Function ToString() As String
Return String.Concat(Me.a, Me.b, Me.c, Me.d, Me.e, Me.f, Me.g)
End Function
End Class
The you could just do:
Textbox1.text = test
There is a way to dynamically get and set the value of properties on any object. Such functionality in .NET is collectively referred to as Reflection. For instance, to loop through all of the properties in an object, you could do something like this:
Public Function GetPropertyValues(o As Object) As String
Dim builder As New StringBuilder()
For Each i As PropertyInfo In o.GetType().GetProperties
Dim value As Object = Nothing
If i.CanRead Then
value = i.GetValue(o)
End If
If value IsNot Nothing Then
builder.Append(value.ToString())
End If
Next
Return builder.ToString()
End Function
In the above example, it calls i.GetValue to get the value of the property, but you can also call i.SetValue to set the value of the property. However, reflection is inefficient and, if used inappropriately, it can lead to brittle code. As such, as a general rule, you should avoid using reflection as long as there is any other better way to do the same thing. In other words, you should typically save reflection as a last resort.
Without more details, it's difficult to say for sure what other options would work well in your particular situation, but I strongly suspect that a better solution would be to use a List or Dictionary, for instance:
Dim myList As New List(Of String)()
myList.Add("first")
myList.Add("second")
myList.Add("third")
' ...
For Each i As String In myList
Textbox1.Text &= i
Next
Or:
Dim myDictionary As New Dictionary(Of String, String)()
myDictionary("a") = "first"
myDictionary("b") = "first"
myDictionary("c") = "first"
' ...
For Each i As KeyValuePair(Of String, String) In myDictionary
Textbox1.Text &= i.Value
Next

How to pass a property to html helper method

I have an html helper class for my webforms projects. so far it can return strings to create labels and readonly fields.
Public Shared Function DisplayFor(value As String, Optional attributes As String = "") As String
Return [String].Format("<span class='uneditable-input {0}'>{1}</span>", GetSStyle(attributes), value)
End Function
Now I want to create some overloads that can accept passing the entity property so it can internally check the datatype (from attributes) and display the content formatted, for example. Just as MVC does.
The only problem it's that I don't know how to pass a class property as a function parameter.
You can pass a property as an Expression(Of Func(Of MyModel, String)) and by that receive an expression in the method that you can analyze and evaluate:
Public Shared Function DisplayFor(Of TModel, TValue)(model As TModel, expr As Expression(Of Func(Of TModel, TValue))) As String
' Retrieve the value dynamically
Dim compExpr = expr.Compile()
Dim value = compExpr.DynamicInvoke(model)
Dim retVal As String
If value Is Nothing Then
retVal = String.Empty
Else
retVal = value.ToString()
End If
' Analyze expression body
Dim memberAccExpr = DirectCast(expr.Body,
System.Linq.Expressions.MemberAccessExpression)
Dim attr = memberAccExpr.Member.GetCustomAttributes(typeof(MyDisplayAttribute), false).Cast(Of MyDisplayAttribute)().FirstOrDefault();
Return retVal
End Function
Call the method like this:
DisplayFor(myModelVar, Function(m) m.MyProperty)
I hope this sample gives you a rough outline on how to handle this. Please note that especially the analysis of the expression body is simplified. In real world code there would be various checks to make sure that the expression matches your expectations.

How can I get a property name for a type without the need to instantiate an object of that type?

I have a requirement where I need to have a "type safe" way of accessing property names, without actually instantiating an object to get to the property. To give an example, consider a method that takes as arguments a list of IMyObject and a string that represents a property name (a property that exists in IMyObject).
The methods implementation will take the list and access all the objects in the list using the property name passed... for some reason or another, we won't dwell on that!!
Now, I know that you can do this using an instantiated object, something like ...
Dim x as MyObject = nothing
Dim prop As PropertyInfo = PropHelper.GetProperty(Of MyObject)(Function() x.MyProperty)
Where my helper method uses reflection to get the name of the property as a string - there are numerous examples of this flying around on the web!
But I don't want to have to create this pointless object, I just want to do something like MyObject.MyProperty! Reflection allows you to iterate through a types properties and methods without declaring an object of that type... but I want to access a specific property and retrieve the string version of its name without iteration and without declaring an object of that type!
The main point here is that although I am trying to get the property name as a string... this is done at run time... at compile time, I want this to be type safe so if someone changes the property name, the compilation will break.
Can anyone help in this quest!?!
So here is a quick code-listing to demonstrate the answer that I was looking for:
Imports System.Linq.Expressions
Public Class A
Public Prop1 As String
Public Prop2 As Integer
End Class
Public Class Form1
Public Function GetPropertyNameB(Of TModel, TProperty)(ByVal [property] As Expression(Of Func(Of TModel, TProperty))) As String
Dim memberExpression As MemberExpression = DirectCast([property].Body, MemberExpression)
Return memberExpression.Member.Name
End Function
Public Sub New()
InitializeComponent()
Dim propertyName As String = GetPropertyNameB(Function(myObj As A) myObj.Prop1)
Dim propertyName2 As String = GetPropertyNameB(Function(myObj As A) myObj.Prop2)
MsgBox(propertyName & " | " & propertyName2)
End
End Sub
End Class
You may be able to pass the property in as a simple lamdba expression, and take it in the method as an expression tree. You should be able to analyze the expression tree to get the string name of the property, but it the lambda expression will fail to compile if the property name changes. Check out this page for more details:
http://msdn.microsoft.com/en-us/library/bb397951.aspx
You can make use of the NameOf function:
Dim fieldName = nameOf(MyClass.MyField)