How do I pass an NLog logger as optional parameter - vb.net

I'm working with code that references a library that has an optional NLog.Logger as a parameter (the code for this library with this method definition was lost so I have to recreate it). The code below complains with this message: "Error 41 Constant expression is required." I can't figure out how to make this work though as I've tried to define a Const, as well as just setting the default value to null and nothing works. How do I create an option parameter of type Logger? Thanks!
Dim objTraceLogDefault As Logger = LogManager.GetLogger("TraceFile")
Public Function MyFunction(Optional ByVal objTraceLog As Logger =
objTraceLogDefault)

Optional parameters should be constants, (e.g. numbers, strings or Nothing), because the compiler add the constant values add compile time.
You could set the default to Nothing and check for Nothing in the body.
Private Function MyFunction(ByVal Optional objTraceLog As Logger = Nothing)
objTraceLog = If(objTraceLog, objTraceLogDefault)
End Function

Related

Cast generic structure to other structure

I'm having trouble wrapping my head around the problem I'm having. I want to apply some general rules to structures, and as the type of them differs, I want to use a generic function to do this. My problem is that to manipulate the structure via methods only available with parameters of a specified type, I can not find a way to do so without extensive casting. See, for example, what steps are needed to specify that a DateTime value should always be specified as UTC:
Public Shared Function Sanitize(Of T As Structure)(retValue As T?) As T?
' If value is DateTime it must be specified as UTC:
If GetType(T) = GetType(DateTime) AndAlso retVal.HasValue Then
' To specify the value as UTC, it must first be casted into DateTime, as it is not know to the compiler that the type in fact IS
' DateTime, even if we just checked.
Dim retValAsObj = CType(retVal, Object)
Dim retValAsObjAsDateTime = CType(retValAsObj, DateTime)
Dim retValWithSpecifiedKind = DateTime.SpecifyKind(retValAsObjAsDateTime, DateTimeKind.Utc)
retVal = CType(CType(retValWithSpecifiedKind, Object), T?)
End If
Return retVal
End Function
Am I missing something? Casting four times for such a simple task seems to complex for me to be the best / simplest solution.
You can use extension methods
With extension method you don't need to check type and cast it.
With extension method you will have own method for every type - simple to maintain
With extension method you will have "readable" syntax
<Extension>
Public Shared Function Sanitize(Date? nullable) AS Date?
{
If nullable.HasValue = False Then Return nullable
Return DateTime.SpecifyKind(nullable.Value, DateTimeKind.Utc)
}
<Extension>
Public Shared Function Sanitize(Integer? nullable) AS Integer?
{
If nullable.HasValue = False Then Return nullable
If nullable.Value < 0 Then Return 0
Return nullable.Value
}
Somewhere in the code
Dim sanitizedDate As Date? = receivedDate.Sanitize()
Dim sanitizedAmount As Integer? = receivedAmount.Sanitize()
Extension methods have some downsides - for example you not able to "mock" them for unit testing, which force you to test "Sanitize" method every time it used (if you are using Test-First approach).

VB.NET unable to differentiate between overloaded functions

Current version of MVVM Light has a helper function named Set in ObservableObject class that an inheriting ViewModel class can call to both change property value and raise change notification in one call. Together with the new NameOf operator, this makes the boilerplate code of properties much smaller.
The problem however is that the Set function is overloaded and out of the 3 overloads, the following 2 overloads make VB.NET angry:
Protected Function [Set](Of T)(propertyName As String, ByRef field As T, newValue As T) As Boolean
Protected Function [Set](Of T)(ByRef field As T, newValue As T, <CallerMemberName> Optional propertyName As String = Nothing) As Boolean
Now if you have a String type property, VB.NET cannot differentiate as to which overload we are calling.
Overload resolution failed because no accessible '[Set]' is most specific for these arguments:
'Protected Overloads Function [Set](Of String)(propertyName As String, ByRef field As String, newValue As String) As Boolean': Not most specific.
'Protected Overloads Function [Set](Of String)(ByRef field As String, newValue As String, [propertyName As String = Nothing]) As Boolean': Not most specific.
Note that C# can handle this situation easily, by using ref keyword. Also that even though the current situation is related to MVVM Light, the problem itself is generic. I have tried to use named parameters too, but that doesn't help either. Any hints on how this could be solved?
Here again after almost a year. I just found a little workaround that would work in most cases. Instead of calling one of the overloads mentioned in the question, use the third overload:
Protected Function [Set](Of T)(ByRef field As T, newValue As T, <CallerMemberName> Optional propertyName As String = Nothing) As Boolean
The third parameter of this overload is optional and if you skip it in the call, it will use CallerMemberName to assign it a value. Since Set is almost always called from within the property, this approach should work nicely. No other overload takes two parameters, so compiler can resolve it correctly.

Const from function

How can such a function be created? (How do the framework do it for Chr)
Or why is it not possible, but possible in the framework?
Example of consts declared from function:
''' <summary>Carriage Return \r 13 0xD</summary>
Public Const Cr As Char = ChrW(&HD)
''' <summary>Line Feed \n 10 0xA</summary>
Public Const Lf As Char = ChrW(&HA)
MS reference source for ChrW
So it should be possible to create similar functions ex:
Public Const Magic As String = "Magic" ' ok
Public Const lcMagic As String = Magic.ToLower ' not ok - wanted to avoiding potential bugs if Magic is ever changed.
Public Const Magic2 As String = functionGeneratingLongButStaticString()
A workaround for most cases is something like:
''' <summary>Lowercase value of Magic</summary>
Public Shared ReadOnly lcMagic As String = Magic.ToLower
But this do not provide intellisense of the actual value.
And not "secure" since it is possible to modify ReadOnly fields with reflection
So is there any way to declare 'const functions' at all, and if not, how do the framework really do it?
Edit:
Checked the generated IL:
.field public static literal char Cr = char('\r')
Which means it is a special case for the compiler.
The compiler may be treating AscW and ChrW as special cases. A search of the Roslyn source code for "AscW" turns up the following in OptimizeLibraryCall (lots of code snipped for clarity):
' AscW(char) / AscW(String)
' all values can be optimized as a literal, except an empty string that produces a diagnostic
If IsWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_Strings__AscWCharInt32, method) OrElse
IsWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_Strings__AscWStringInt32, method) Then
'[...]
Return ConstantValue.Create(AscW(argumentValue))
End If
' ChrW
' for -32768 < value or value > 65535 we show a diagnostic
If IsWellKnownTypeMember(WellKnownMember.Microsoft_VisualBasic_Strings__ChrWInt32Char, method) Then
'[...]
Return ConstantValue.Create(ChrW(argumentValue))
End If
'[...]
There's also a comment in EarlyWellKnownAttributeBinder.CanBeValidAttribute that specifically mentions AscW and ChrW in the types of compile-time constants that can be used for attribute values:
' 11.2 Constant Expressions
'
'A constant expression is an expression whose value can be fully evaluated at compile time.
' [...]
' The following run-time functions:
' Microsoft.VisualBasic.Strings.ChrW
' Microsoft.VisualBasic.Strings.Chr, if the constant value is between 0 and 128
' Microsoft.VisualBasic.Strings.AscW, if the constant string is not empty
' Microsoft.VisualBasic.Strings.Asc, if the constant string is not empty
The Mono vbnc compiler seems to have an IsConstantMethod helper method to support these functions (it specifically checks whether the method is AscW, Asc, ChrW, or Chr), along with a test case specifically to test whether const statements accept those functions, which suggests they are special cases.
If you look at your compiled assembly in ildasm, you'll see:
.field public static literal char Cr = char(0x000D)
You can see that the compiler is evaluating the function and emitting the result -- it's not embedding any sort of "constant function" call, and there's not any sort of attribute decoration on those functions in the reference source that marks them as a "constant function".
So to answer your questions, it looks like the compiler does this by treating those string functions as special hardcoded cases, and it doesn't look like there's any way to do so, short of modifying the compiler code.
From MSDN:
You cannot use variables or functions in initializer. However, you can use conversion keywords such as CByte and CShort. You can also use AscW if you call it with a constant String or Char argument, since that can be evaluated at compile time.
ChrW etc are special because the compiler is designed to allow them:
Private Const vblf As String = ChrW(10)
Compiles to:
private const char vblf = '\n';
Most other forms using NET function are illegal:
Private Const vbcr As String = Convert.ToString(New Byte() {13})
Private Const vblf2 As Char = Environment.NewLine
Private Const Z As String = Ziggy.ToLower
Even if Ziggy is a constant, making it seem like it should be legal, the function result is not. The return could be different on a non-English system making for very nasty issues at runtime when strings do not match but should.

Create Data.SqlClient.SqlParameter with a SqlDataType, Size AND Value inline?

I have a function which can take a number of SqlParameter objects:
Public Shared Function RunSQL(sqlInput As String, Optional params As Data.SqlClient.SqlParameter() = Nothing) As String
The aim is basically being able to call the function and create the parameters if needed, as needed.
Unfortunately, there do not appear to be any constructors available that will allow me to specify the Value as well as the SqlDbType without having to add lots of additional parameters (that I do not need) as well.
The desired outcome would be something like:
Dim myStr As String = RunSQL("spMyStoredProc", {New Data.SqlClient.SqlParameter("#FieldName", Data.SqlDbType.NVarChar, 50, "MyValue")})
Obviously as the constructor for this does not exist, my question is basically to ask whether or not there is any way around this, as in any alternative, etc whilst still allowing the convenience of declaring the parameters in the function call?
I'd rather not have to declare all of my parameters beforehand just to set the Value property.
First, your code compiles but the last parameter is not the value but the source-column-name.
But you could use the With statement to assign the Value:
Dim myStr As String = RunSQL("spMyStoredProc",
{
New Data.SqlClient.SqlParameter("#FieldName", Data.SqlDbType.NVarChar, 50) With {.Value = "MyValue"}
})
See: Object Initializers

Cannot get lambda code sample to compile

I want to experiment with a simple bit of code found here:
http://msdn.microsoft.com/en-au/library/ff664617%28v=pandp.50%29.aspx
But I cannot get it to compile, I must be doing something wrong. About a third of the way down the page it gives a code sample like this:
'Usage exManager.Process(Function() method-name(param1, param2), _
"Exception Policy Name")
But if I enter the following code:
Dim exManager As ExceptionManager
exManager = EnterpriseLibraryContainer.Current.GetInstance(Of ExceptionManager)()
exManager.Process(Function() TestSub(), "Exception Policy Name")
I get an error on the third line that says:
Overload resolution failed because no accessible 'Process' can
be called with these arguments:
'Public Overridable Function Process(Of TResult)(action As System.Func(Of TResult), policyName As String) As TResult': 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 Overridable Function Process(Of TResult)(action As System.Func(Of TResult), policyName As String) As TResult': Data
type(s) of the type parameter(s) cannot be inferred from these
arguments. Specifying the data type(s) explicitly might correct this
error.
etc.
Even if I try to modify the lambda like this:
exManager.Process(Function() Dim A As Integer=6, "Exception Policy Name")
I get a similar error.
Any comments would be appreciated.
UPDATE:
Note I am compiling for .NET Framework 4 Client Profile
You are using the Process(Of TResult) overload when you call exManager.Process(Function() TestSub(), "Exception Policy Name").
The method is generic and expects and type argument (TResult), which in a lot of cases can be inferred by the compiler. Now the exception tells you that, in your case, the compiler can infer TResult.
I guess your TestSub is really a Sub and therefore has no return value which the compiler could use to infer TResult.
So use the non-generic Process method by either using Sub instead of Function
exManager.Process(Sub() TestSub(), "Exception Policy Name")
or simply use AddressOf:
exManager.Process(AddressOf TestSub, "Exception Policy Name")