Naming variables describing the same thing but of different type - naming-conventions

What is a commonly accepted approach in variable naming when dealing with variables that are of different type but describe the same thing?
private String contentFolder = "/tmp/";
private File contentDirectory = new File(contentFolder);
The above seems sloppy. But what is better?
private String contentDirectoryStr = "/tmp/";
private File contentDirectory = new File(contentDirectoryStr);
Looks just as bad.
What is a common convention you follow to describe same things of different type?
Thought you can of course get String from File, for the purposes of this question, assume you legitimately need both a String and a File in your class.

My first thought would be to use the same name, but differentiate based on the type.
Example:
private String contentDirStr = "/tmp/";
private File contentDirFile = new File(contentDirStr);
Of course it would better to not have two objects representing the same thing, but if you really need to, that's how I would go about naming them.

Related

Using Global Constants to store environment variables

I'm creating an add-in for Microsoft Excel and I'm using a module to store variables (which do not change during runtime) which are accessed by classes and other modules in the app. This is a more convenient way of hard coding values which I may have to find and replace at different points in the development cycle.
The variables are things such as the app version, or the workbook password:
Option Explicit
Option Private Module
Global Const APP_ID = "XXY"
Global Const APP_VERSION = "1.00"
Global Const WORKSHEET_PASSWORD = "s0Me_pa$$w0rD"
Global Const DATA_TABLE = "tblStockData"
Global Const FORM_DEFAULT_VALUE = 200
I was wondering if anyone can tell me if this is bad practice and if so, what a better course of action would be?
I usually do exactly the same or two other things, depending on the size of the application. If it is something small, I use your approach. However,these are the other approaches I use.
First Approach:
If the user is supposed to change the password or the default_value, I make a separate sheet, called "Settings" and I write them there. Then I make a public function for the password like this:
public function GetPassword as string
GetPassword = [set_password]
end function
Second Approach:
If this is a big application with lots of constant and public variables, the scope gets somehow "dirty" and it is difficult to find the variables. Thus, I make a class of the Constants and I declare them there. Then, it is easier to call them.
Something like this:
Private Const p_First_COLUMN_TO_FILL = 8
Public Property Get FIRST_COLUMN_TO_FILL() As Long
FIRST_COLUMN_TO_FILL = p_First_COLUMN_TO_FILL
End Property
Thus, if my class is clsConstants, I declare it as Public objCon As clsConstants and now with objCon.FIRST_COLUMN_TO_FILL I get what I want. The scope is somehow cleaner this way. You may also build more than one class of constants, depending on what are they used for.

Const Variable in "Type"-Statement vba

I want to sum up my Const Variables in a VBA Makro like this:
Private Type Company
Public Const CompanyNameColumns As String = "14"
Public Const CompanyNameStartRow As Integer = 5
Type End
I can't run this code.
I think the problem is, that there is no possibility of defining a Const within a Type Statement.
Has anybody a workaround for that?
So, it took me a second, but then it smacked me like a ton of bricks what's going on here.
TL;DR: You can't assign values during definition of a type, but there is a proper way to do what you're trying to do.
The reason you can't assign to it is because you're defining a type. I know that sounds cyclical and redundant, but that's exactly what is going on. You're trying to assign values in a place that is meant to define a data structure. That's all a UDT does. It defines a structure. It makes no sense to assign values to a structure.
As you've found, one solution is to create a new module and store your constants there.
Constants.bas
Public Const CompanyNameColumns As String = "14"
Public Const CompanyNameStartRow As Integer = 5
Which then gets called like this...
Constants.CompanyNameColumns
Constants.StartNameRow
And this is fine, but runs the risk of becoming a catch all. It would be much better to move these into a class module along with the logic they relate to. That way all of the related logic and data are in one place instead of scattered about multiple *.bas files in your project.
If you're going the class route, but the classes only hold this data structure, without any real state, you might want to consider making them global default instances. This is similar to what is known as "static" classes in other languages.
I worked around this Problem now by just defining a new Module. Named it Company and put the Consts in there!

Comparing String to class name

I have a little Problem:
I have a method that parses an incoming string for certain values, if a value is found, a new class is instantiated. The class name is identical to the parsed string. At the moment, my code looks like this:
Public Class Test1
End Class
Public Class Important
End Class
Public Class DoWork
Public Sub DoWork(incoming as String)
Select case incoming
case "Test1"
dim myobj as new Test1
Case "Important"
dim myobj as new Important
End Select
End Sub
End Class
I do not like the string literals like "Test1" - i could store them in a constant, but if the class names change, they have to be changed too. Is there a way to replace the literals with the Name of class?
I know that me.gettype produces the result for instantiated objects, but what about the simple name for a class, which is no object at this moment?
If your string is in correct format you can use Type.GetType(string) method to retrieve type. Then you can use Activator class to create instance if you have default constructor on that type.
Rafal's answer is good if you're stuck with the current situation, with the incoming string parameter. But it's still a bit fragile. What if the incoming parameter changes? What if you want to restructure your code, moving some classes to different namespaces or assemblies? What if those strings change - do you now have to rename your classes and recompile? You don't see the magic strings explicitly now, but they're still there.
So ask yourself - where are those strings coming from? Are they generated internally by your code? If so, you might want to generate, instead of strings, an Enum value that corresponds to the class to be instantiated. If they're external strings that you map to your classes, consider having explicit mapping (in a configuration file, for instance) that map String->Type. It's a bit more cumbersome, but a lot more flexible.

How can I create an "enum" type property value dynamically

I need to create a property in a class that will give the programmer a list of values to choose from. I have done this in the past using the enums type.
Public Enum FileType
Sales
SalesOldType
End Enum
Public Property ServiceID() As enFileType
Get
Return m_enFileType
End Get
Set(ByVal value As enenFileType)
m_enFileType = value
End Set
End Property
The problem is I would like to populate the list of values on init of the class based on SQL table values. From what I have read it is not possible to create the enums on the fly since they are basically constants.
Is there a way I can accomplish my goal possibly using list or dictionary types?
OR any other method that may work.
I don't know if this will answer your question, but its just my opinion on the matter. I like enums, mostly because they are convenient for me, the programmer. This is just because when I am writing code, using and enum over a constant value gives me not only auto-complete when typing, but also the the compile time error checking that makes sure I can only give valid enum values. However, enums just don't work for run-time defined values, since, like you say, there are compile time defined constants.
Typically, when I use flexible values that are load from an SQL Table like in your example, I'll just use string values. So I would just store Sales and SalesOldType in the table and use them for the value of FileType. I usually use strings and not integers, just because strings are human readable if I'm looking at data tables while debugging something.
Now, you can do a bit of a hybrid, allowing the values to be stored and come from the table, but defining commonly used values as constants in code, sorta like this:
Public Class FileTypeConstants
public const Sales = "Sales"
public const SalesOldType = "SalesOldType"
End Class
That way you can make sure when coding with common values, a small string typo in one spot doesn't cause a bug in your program.
Also, as a side note, I write code for and application that is deployed internally to our company via click-once deployment, so for seldom added values, I will still use an enum because its extremely easy to add a value and push out an update. So the question of using and enum versus database values can be one of how easy it is to get updates to your users. If you seldom update, database values are probably best, if you update often and the updates are not done by users, then enums can still work just as well.
Hope some of that helps you!
If the values aren't going to change after the code is compiled, then it sounds like the best option is to simply auto-generate the code. For instance, you could write a simple application that does something like this:
Public Shared Sub Main()
Dim builder As New StringBuilder()
builder.AppendLine("' Auto-generated code. Don't touch!! Any changes will be automatically overwritten.")
builder.AppendLine("Public Enum FileType")
For Each pair As KeyValuePair(Of String, Integer) In GetFileTypesFromDb()
builder.AppendLine(String.Format(" {0} = {1}", pair.Key, pair.Value))
End For
builder.AppendLine("End Enum")
File.WriteAllText("FileTypes.vb", builder.ToString())
End Sub
Public Function GetFileTypesFromDb() As Dictionary(Of String, Integer)
'...
End Function
Then, you could add that application as a pre-build step in your project so that it automatically runs each time you compile your main application.

Create a "clone" of this object, not point to it

Let's say I got a list called
myFirstList
And then I want to create a copy of that list so I can do some tweaks of my own. So I do this:
mySecondList = myFirstList
mySecondList.doTweaks
But I noticed that the tweaks also affect the myFirstList object! I only want the tweaks to affect the second one...
And afterwards I will want to completely delete mySecondList, so I do mySecondList = Nothing and I'm good, right?
Adam Rackis, I don't like your "Of course it does", because it is not at all obvious.
If you have a string variable that you assign to another string variabe, you do not change them both when making changes to one of them. They do not point to the same physical piece of memory, so why is it obvious that classes do?
Also, the thing is not even consistent. In the following case, you will have all elements in the array pointing at the same object (they all end up with the variable Number set to 10:
SourceObject = New SomeClass
For i = 1 To 10
SourceObject.Number = i
ObjectArray.Add = SourceObject
Next i
BUT, the following will give you 10 different instances:
For i = 1 To 10
SourceObject = New SomeClass
SourceObject.Number = i
ObjectArray.Add = SourceObject
Next i
Apparently the scope of the object makes a difference, so it is not at all obvious what happens.
Here is how you do it:
'copy one object to another via reflection properties
For Each p As System.Reflection.PropertyInfo In originalobject.GetType().GetProperties()
If p.CanRead Then
clone.GetType().GetProperty(p.Name).SetValue(clone, p.GetValue(OriginalObject, Nothing))
End If
Next
in some cases when the clone object got read-only properties you need to check that first.
For Each p As System.Reflection.PropertyInfo In originalobject.GetType().GetProperties()
If p.CanRead AndAlso clone.GetType().GetProperty(p.Name).CanWrite Then
clone.GetType().GetProperty(p.Name).SetValue(clone, p.GetValue(OriginalObject, Nothing))
End If
Next
Since you have not divulged the type of item that you are storing n your list, I assume it's something that's implementing IClonable (Otherwise, if you can, implement IClonable, or figure out a way to clone individual item in the list).
Try something like this
mySecondList = myFirstList.[Select](Function(i) i.Clone()).ToList()
But I noticed that the tweaks also
affect the myFirstList object! I only
want the tweaks to affect the second
one...
Of course it does. Both variables are pointing to the same object in memory. Anything you do to the one, happens to the other.
You're going to need to do either a deep clone, or a shallow one, depending on your requirements. This article should give you a better idea what you need to do
Expanding on Adam Rackies' answer I was able to implement the following code using VB.NET.
My goal was to copy a list of objects that served mainly as data transfer objects (i.e. database data). The first the class dtoNamedClass is defined and ShallowCopy method is added. A new variable named dtoNamedClassCloneVar is created and a LINQ select query is used to copy the object variable dtoNamedClassVar.
I was able to make changes to dtoNamedClassCloneVar without affecting dtoNamedClassVar.
Public Class dtoNamedClass
... Custom dto Property Definitions
Public Function ShallowCopy() As dtoNamedClass
Return DirectCast(Me.MemberwiseClone(), dtoNamedClass)
End Function
End Class
Dim dtoNamedClassVar As List(Of dtoNamedClass) = {get your database data}
Dim dtoNamedClassCloneVar =
(From d In Me.dtoNamedClass
Where {add clause if necessary}
Select d.ShallowCopy()).ToList
Here's an additional approach that some may prefer since System.Reflection can be slow.
You'll need to add the Newtonsoft.Json NuGet package to your solution, then:
Imports Newtonsoft.Json
And given a class type of MyClass, cloning can be as easy as:
Dim original as New MyClass
'populate properties of original...
Dim copy as New MyClass
copy = JsonConvert.DeserializeObject(Of MyClass)(JsonConvert.SerializeObject(original))
So the approach is to first use the JSON converter to serialize the original object, and than take that serialized data and deserialize it - specifying the class type - into the class instance copy.
The JSON converters are extremely powerful and flexible; you can do all sorts of custom property mappings and manipulations if you need something the basic approach above doesn't seem to address.
this works for me:
mySecondList = myFirstList.ToList
clone is the object you are attempting to clone to.
dim clone as new YourObjectType
You declare it like that.