How to make several similar properties call one generic one - vb.net

I'm wondering if it's possible in VB.NET to make similar properties call one generic one?
A sentence doesn't explain it well so here's a code example.
I have a bit field defined like this:
<Flags()> _
Enum E_Operation As Integer
Upload = 1
Download = 2
Overwrite = 4
etc...
End Enum
Now my class has one property per bit in the bit field. Each property just returns the value or sets the corresponding flag. e.g.
Public Property IsUpload() As Boolean
Get
Return ((Operation And E_Operation.Upload) = E_Operation.Upload)
End Get
Set(ByVal value As Boolean)
SetBit(E_Operation.Upload, value)
End Set
End Property
Now I have quite a lot of properties and I would like to simplify them (ideally just one line) by calling a generic property with the bit number to Set or Get.
Public Property IsUpload() As Boolean
GenericProperty(E_Operation.Upload)
End Property
Is there any way to achieve this in VB.NET?

You can make the enumeration a parameter in a private property:
Private Property OperationFlag(Flag As E_Operation) As Boolean
Get
Return ((Operation And Flag) = Flag)
End Get
Set(ByVal value As Boolean)
Operation = (Operation And Not Flag) Or (value And Flag)
End Set
End Property
And make a public property wrapper:
Public Property IsUpload As Boolean
Get
Return OperationFlag(E_Operation.Upload)
End Get
Set(value As Boolean)
OperationFlag(E_Operation.Upload) = value
End Set
End Property

Related

Can't iterate through each property of a custom object

I have this class:
Public Class clsServCasam
Public ID As Long
Public CANT As Decimal
Public PRICE As Decimal
End Class
I create a variable of that type and get the object from an API result:
Dim myObj As clsServCasam()
Dim rsp As HttpWebResponse = CType(rq.GetResponse(), HttpWebResponse)
If rsp.StatusCode = HttpStatusCode.OK Then
Using sr = New StreamReader(rsp.GetResponseStream())
myObj = JsonConvert.DeserializeObject(Of clsServCasam())(sr.ReadToEnd())
End Using
Then I try to get the field names from the object:
For Each p As System.Reflection.PropertyInfo In myObj.GetType().GetProperties()
Debug.Print(p.Name, p.GetValue(myObj, Nothing))
Next
But, instead of class fields (ID, PRICE, ...) I got:
- Length
- LongLength
- Rank
Update
As Steven Doggart pointed out, the above loop won't work because it looks for properties rather than fields. So, I tried changing the loop to this:
For Each p As FieldInfo In myObj.GetType.GetFields()
Debug.Print(p.Name)
Next
But now I'm getting no results at all.
In your code, myObj is not declared as clsServCasam. Rather, it is declared as clsServCasam(), which means it's an array of clsServCasam objects. So, when you use reflection to iterate over its properties, you're getting the properties of the array rather than the actual clsServCasam type.
For instance, this would work more like you're expecting:
For Each item As clsServCasam in myObj
For Each p As PropertyInfo In item.GetType().GetProperties()
Debug.Print(p.Name, p.GetValue(item, Nothing))
Next
Next
However, I think you'll find that that still won't work because it iterates over the properties rather than the fields. In the definition of the clsServCasam class, all of the members are fields rather than properties, so the only properties that it have would be ones that are inherited from Object. You will need to either iterate over the fields using GetFields, like this:
For Each item As clsServCasam in myObj
For Each f As FieldInfo In item.GetType().GetFields()
Debug.Print(f.Name, f.GetValue(item))
Next
Next
Or you'll need to change them to properties:
Public Class clsServCasam
Public Property ID As Long
Public Property CANT As Decimal
Public Property PRICE As Decimal
End Class
Or, if you are using an older version of the VB compiler which doesn't support auto-properties:
Public Class clsServCasam
Public Property ID As Long
Get
Return _id
End Get
Set(value As Long)
_id = value
End Set
End Property
Public Property CANT As Decimal
Get
Return _cant
End Get
Set(value As Decimal)
_cant = value
End Set
End Property
Public Property PRICE As Decimal
Get
Return _price
End Get
Set(value As Decimal)
_price = value
End Set
End Property
Private _id As Long
Private _cant As Decimal
Private _price As Decimal
End Class

Predefined class properties if need expand or shrink quantity of them on each app start

Public Class Products
Private zCriteria As String
Public Property Criteria As String
Get
Return zCriteria
End Get
Set(value As String)
zCriteria = value
End Set
End Property
Private zProductList As New List(Of Product)
Public Property ProductList As List(Of Product)
Get
Return zProductList
End Get
Set(value As List(Of Product))
zProductList = value
End Set
End Property
End Class
Public Class Product
Private zCriteriaList As List(Of Criterias)
Public Property CriteriaList As List(Of Criterias)
Get
Return zCriteriaList
End Get
Set(value As List(Of Criterias))
zCriteriaList = value
End Set
End Property
End Class
Public Class Criterias
Private zCrPropName As String
Public Property CrPropName As String
Get
Return zCrPropName
End Get
Set(value As String)
zCrPropName = value
End Set
End Property
Private zCritCode As String
Public Property CritCode As String
Get
Return zCritCode
End Get
Set(value As String)
zCritCode = value
End Set
End Property
Private zcrPropValue As String
Public Property crPropValue As String
Get
Return zcrPropValue
End Get
Set(value As String)
zcrPropValue = value
End Set
End Property
End Class
For Each oProducts As Products In oAssigment.ProductsList
For Each oproduct As Product In oProducts.ProductList
For Each cr As Criterias In oproduct.CriteriaList
cr.CrPropName = "Product Name" 'some object property name
cr.CritCode = "PN"
cr.crPropValue = "" ' Value of property "Product Name"
Next
Next
Next
It is all made to have different properties of some object depending on options set in text file. It is only a sample o usage.
conditions:
Criterias on applications start is same for all objects (=Product) i want to read.
Every time on start, application read options file where is defined few property names and codes (values i want get from objects). So every run can be initiated different quantity of properties to read. It means i can not have hard-coded names of properties.
Someone can advice me how to predefine "CrPropName" and "CritCode" on app start so many times how much property names defined in options and after that populate it so many times as how many objects exist from which i read these property values.
p.s. I am not very professional at coding and sorry for my language

Can't serialize using DataContractSerializer

I have tried the following code:
PolicyProcessRequest.BranchCode = "HeadOff"
PolicyProcessRequest.Policy.BranchCode = "HeadOff"
PolicyProcessRequest.Policy.Risks.Item(0).BranchCode = "HeadOff"
Dim dcs As DataContractSerializer = New DataContractSerializer(GetType(PureMessagingService.PolicyProcessRequestType))
Dim ms As New MemoryStream()
dcs.WriteObject(ms, PolicyProcessRequest)
Am getting the following exception on the call to WriteObject
System.Runtime.Serialization.SerializationException was caught
HResult=-2146233076 Message=Member BranchCode in type Sirius.SBO.Import.PureMessagingService.BaseRequestType cannot be serialized.
This exception is usually caused by trying to use a null value where a null value is not allowed.
The 'BranchCode' member is set to its default value (usually null or zero). The member's EmitDefault setting is 'false', indicating that the member should not be serialized.
However, the member's IsRequired setting is 'true', indicating that it must be serialized. This conflict cannot be resolved. Consider setting 'BranchCode' to a non-default value. Alternatively, you can change the EmitDefaultValue property on the DataMemberAttribute attribute to true, or changing the IsRequired property to false.
Yet I've set the 'BranchCode' property to the non default value everywhere in the request.
Public Class BaseNBQuoteRequestType
Private agentCodeField As String
Private branchCodeField As String
Private currencyCodeField As CurrencyType
Private currencyCodeFieldSpecified As Boolean
Private itemField As BasePartyType
Private policyField As BaseQuoteRiskMsgType
Private updatePartyField As Boolean
Public Property AgentCode() As String
Get
Return Me.agentCodeField
End Get
Set(ByVal value As String)
Me.agentCodeField = value
End Set
End Property
Public Property BranchCode() As String
Get
Return Me.branchCodeField
End Get
Set(ByVal value As String)
Me.branchCodeField = value
End Set
End Property
Public Property CurrencyCode() As CurrencyType
Get
Return Me.currencyCodeField
End Get
Set(ByVal value As CurrencyType)
Me.currencyCodeField = value
End Set
End Property
Public Property CurrencyCodeSpecified() As Boolean
Get
Return Me.currencyCodeFieldSpecified
End Get
Set(ByVal value As Boolean)
Me.currencyCodeFieldSpecified = value
End Set
End Property
Public Property Party() As BasePartyType
Get
Return Me.itemField
End Get
Set(ByVal value As BasePartyType)
Me.itemField = value
End Set
End Property
Public Property Policy() As BaseQuoteRiskMsgType
Get
Return Me.policyField
End Get
Set(ByVal value As BaseQuoteRiskMsgType)
Me.policyField = value
End Set
End Property
Public Property UpdateParty() As Boolean
Get
Return Me.updatePartyField
End Get
Set(ByVal value As Boolean)
Me.updatePartyField = value
End Set
End Property
End Class
I have the same problem, and it comes from the DataContractSerializer which is used to generate the code. I have other services which use the XmlSerializer with no problem.
Unfortunatly, when using Svcutil.exe or the 'Add Service Reference' feature in Visual Studio to generate client code, an appropriate serializer is automatically selected for you. If the schema is not compatible with the DataContractSerializer, the XmlSerializer is selected (source).
So, you have to manualy fix the IsRequired setting in references.cs each time you generate it.
Replace
IsRequired=true
by
IsRequired=false
in the references.cs file.

How to iterate through a property of a class in VB?

If I have a class object A, and it has properties such as a0, a1, a2... If this class has 100 properties like this (up to a99). I would like to display each of these properties, but I do not want to have 100 lines of code of calling this as following
print A.a0
print A.a1
print A.a2
...
print A.a99
The code is too inefficient, so I am wondering if there is a way to loop through these properties. Thank you.
.NET provides the ability to examine an object at runtime through a process known as reflection. The purpose of the original post was to iterate through an object's properties in an automated fashion rather than by manually coding explicit statements that displayed each property, and reflection is a process to accomplish this very thing.
For this particular purpose, looping through an object's properties at run-time, you use the GetProperties() method that is available for each Type. In your case, the Type you want to "reflect" is A, so the type-specific version of GetProperties returns a list of the instance properties for that object.
When you ask .NET to return the properties of an object, you can also specify what's called a binding flag that tells .NET which properties to return - public properties, private properties, static properties - a myriad of combinations from about twenty different values in the BindingFlags enumeration. For the purposes of this illustration, BindingFlags.Public will suffice, assuming your A0-A999 properties are declared to be public. To expose even more properties, simply combine multiple BindingFlag values with a logical "or".
So, now armed with that information, all we need to do is create a class, declare its properties, and tell Reflection to enumerate the properties for us. Assuming your Class A exists with property names A0-A999 already defined, here's how you'd enumerate ones starting with "A":
// Assuming Class "A" exists, and we have an instance of "A" held in
// a variable ActualA...
using System.Reflection
// The GetProperties method returns an array of PropertyInfo objects...
PropertyInfo[] properties = typeof(ActualA).GetProperties(BindingFlags.Public);
// Now, just iterate through them.
foreach(PropertyInfo property in properties)
{
if (property.Name.StartsWith("A")){
// use .Name, .GetValue methods/props to get interesting info from each property.
Console.WriteLine("Property {0}={1}",property.Name,
property.GetValue(ActualA,null));
}
}
There you have it. That's C# version rather than VB, but I think the general concepts should translate fairly readily. I hope that helps!
This MSDN code sample illustrates how to iterate over a class's properties using reflection:
http://msdn.microsoft.com/en-us/library/kyaxdd3x.aspx#Y900
Create a VB.Net Console application, copy and paste this code into the Module1.vb file and run it.
Module Module1
Sub Main()
For Each prop In GetType(TestClass).GetProperties()
Console.WriteLine(prop.Name)
Next
Console.ReadKey(True)
End Sub
End Module
Public Class TestClass
Private _One As String = "1"
Public Property One() As String
Get
Return _One
End Get
Set(ByVal value As String)
_One = value
End Set
End Property
Private _Two As Integer = 2
Public Property Two() As Integer
Get
Return _Two
End Get
Set(ByVal value As Integer)
_Two = value
End Set
End Property
Private _Three As Double = 3.1415927
Public Property Three() As Double
Get
Return _Three
End Get
Set(ByVal value As Double)
_Three = value
End Set
End Property
Private _Four As Decimal = 4.4D
Public Property Four() As Decimal
Get
Return _Four
End Get
Set(ByVal value As Decimal)
_Four = value
End Set
End Property
End Class

Is this some kind of referencing problem? Value not sticking

Apologies for the appalling title.
I have mocked up this code to mimic an issue I was encountering on a project.
I want to know why the property status does not 'stick'. Stepping through the code I can even see it setting the property!
Is it something to do with Structure being a value type?
Here is the code, it is standalone.
Imports System.Diagnostics
Public Class clsTest
Public Shared Sub test()
Dim myHolder As New holder
myHolder.info = New info(5)
With myHolder.info
Debug.Print("Initialised Status : {0}", .status)
Debug.Print("Initialised Original Status : {0}", .originalStatus)
myHolder.setStatusToTen()
Debug.Print("Next Status : {0}", .status)
Debug.Print("Next Original Status : {0}", .originalStatus)
End With
End Sub
End Class
Public Class holder
Private _heldInfo As info
Public Property info() As info
Get
Return _heldInfo
End Get
Set(ByVal value As info)
_heldInfo = value
End Set
End Property
Public Sub setStatusToTen()
_heldInfo.status = 10
End Sub
End Class
Public Structure info
Private _iOriginalStatus, _iStatus As Integer
Public Sub New(ByVal iStartingStatus As Integer)
_iOriginalStatus = iStartingStatus
End Sub
Public ReadOnly Property originalStatus() As Integer
Get
Return _iOriginalStatus
End Get
End Property
Public Property status() As Integer
Get
Return _iStatus
End Get
Set(ByVal value As Integer)
_iStatus = value
End Set
End Property
End Structure
When I run clsTest.test I get the following output-
clsTest.test
Initialised Status : 0
Initialised Original Status : 5
Next Status : 0
Next Original Status : 5
...even though setStatusToTen is doing exactly what it says on the tin!
This is yet another case of mutable structs being evil. Avoid using structures for this purpose. As a reference:
Why are mutable structs “evil”?
Yes, its because Structures are value types. When a struct object is assigned to another struct object, the full content of the struct is copied and duplicated. (See this C# example)
So in your code, this set:
Set(ByVal value As info)
_heldInfo = value
End Set
... effectively results in two copies of the structure being made. One is internal to holder and is represented by _heldInfo, and the other is 'external' to holder, and is represented by myHolder.info. After the copy has been made, when you set values on one of them, the other is not affected.
If you add this method to holder:
Public Sub printStatus()
Debug.Print("Internal Status : {0}", _heldInfo.status)
End Sub
... you will find where your value of 10 went.