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.
Related
I am using Dapper to query a flat list of items from a database, into a POCO class as follows:
Public Class Node
Public Property Name As String
Public Property ParentNodeName As String
Public Property Children As IEnumerable(Of Node)
End Class
I am trying to use the accepted answer to this question, in order to create a tree out of the flat list.
The only caveat is that I am using VB.NET.
I have tried it a straightforward port of the C# solution:
nodes.ForEach(Function(n) n.Children = nodes.Where(Function(ch) ch.ParentNodeName = n.Name).ToList)
but it does not compile with the error
Error BC30452 Operator '=' is not defined for types 'List(Of Node)' and 'List(Of Node)'.
The = symbol is interpreted as an equality operator, while I meant to use the assignment operator.
I have pasted the C# code into the telerik converter, and the converted code is:
Private Shared Function BuildTree(ByVal items As List(Of Category)) As IEnumerable(Of Category)
items.ForEach(Function(i) CSharpImpl.__Assign(i.Categories, items.Where(Function(ch) ch.ParentId = i.Id).ToList()))
Return items.Where(Function(i) i.ParentId Is Nothing).ToList()
End Function
Private Class CSharpImpl
<Obsolete("Please refactor calling code to use normal Visual Basic assignment")>
Shared Function __Assign(Of T)(ByRef target As T, value As T) As T
target = value
Return value
End Function
End Class
It uses an helper class to solve this issue, but suggests a refactor to avoid this.
Hence the questions:
Is there a general way to disambiguate equality = and assignment = in VB.NET, without resorting to an helper class and a specific function to assignement
In this specific case, is there a simple refactor I can use to get rid of the issue?
That's because of VB.Net distinction between functions and subroutines.
Instead of
nodes.ForEach(Function(n) n.Children = nodes.Where(Function(ch) ch.ParentNodeName = n.Name).ToList)
use
nodes.ForEach(Sub(n) n.Children = nodes.Where(Function(ch) ch.ParentNodeName = n.Name).ToList)
When you use Function, the lambda expression is expected to return a value; and in your case it looks like it wants to return a boolean.
But you want to use a lambda expression that does not return anything (in your case, you want an assignment), you have to use Sub.
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
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).
I have a c++ dll that I am using using Pinvoke.
The method signature is as follow:
c++:
EXTERN_C BOOL std_call MyCppMethod(LPCSTR param1, LPCSTR param2);
C#:
[DllImport("MyDll.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool MyCppMethod(
/*[MarshalAs(UnmanagedType.LPWStr)]*/ string param1,
/*[MarshalAs(UnmanagedType.LPWStr)]*/ string param2
);
for technical reasons I replaced the dll with a lib, and wrapped the lib with a dll that uses EXPORTS.def file and EXPORTS declaration to export the method from the lib.
Since I have dine that I see a weird behavior. instead of getting the string in the method implementation I get only the first character.
I had tried to replace the calling convention, to use marshalAs LPCWSTR, and also tried to replace the lpcstr in the c++ decleration with char*.
non of the above helped me to solve the problem and I still get only the first character.
Why this is happening and how can I solve this problem?
LPCSTR is a pointer to null-terminated array of char, and UnmanagedType.LPWStr is a pointer to null-terminated array of wchar_t. So there is a simple mis-match.
The reason why you only received a single character is that ASCII characters, when represented as two byte UTF-16 characters, have the ASCII value in one byte, and a zero in the other byte. When interpreted as null-terminated array of char, this is a string of length one. The zero byte is interpreted as the null-terminator.
You can fix it by changing either:
LPCSTR to LPCWSTR on the native side, or
UnmanagedType.LPWStr to UnmanagedType.LPStr on the managed side.
Frankly, to me it makes more sense to use Unicode these days so I would go for option 1. Indeed, since you specified CharSet.Unicode then there is no need for the MarshalAs at all. The code would look like this:
C++
BOOL std_call MyCppMethod(LPCWSTR param1, LPCWSTR param2);
C#
[DllImport("MyDll.dll", CharSet = CharSet.Unicode)]
public static extern bool MyCppMethod(string param1, string param2);
Note that I am also sceptical of your setting SetLastError to true. Does your function really call SetLastError?
In order to return a value from a VB.NET function one can assign a value to the "Functions Name" or use "return value."
I sometimes see these inter-mixed in the same function. Personally, I prefer the return.
My question is, what is the internal difference, if any, between the two?
The difference is that they DO DIFFERENT THINGS!
'Return value' does 2 things:
1. It sets the function return value at that point
2. It immediately exits the function
No further code in the function executes!
'Functionname = value' does 1 thing:
1. It sets the function return value at that point
Other code in the function continues to execute
This enables additional logic to refine or override the function return value
Huge difference folks. Remember it's not all about state, it's also about flow.
Let's take a look... Oddly the "functionName =" generates less IL?
Code:
Public Function Test() As String
Test = "Test"
End Function
Public Function Test2() As String
Return "Test"
End Function
IL:
.method public static string Test() cil managed
{
.maxstack 1
.locals init (
[0] string Test)
L_0000: nop
L_0001: ldstr "Test"
L_0006: stloc.0
L_0007: ldloc.0
L_0008: ret
}
.method public static string Test2() cil managed
{
.maxstack 1
.locals init (
[0] string Test2)
L_0000: nop
L_0001: ldstr "Test"
L_0006: stloc.0
L_0007: br.s L_0009
L_0009: ldloc.0
L_000a: ret
}
There is probably no difference. IIRC, the compiler generated IL converts them both into Return statements unless there is additional usage of a _returnValue variable.
The readability of the FunctionName assignment is poor in my opinion, and an example of a bad VB6 habit. I prefer the _returnValue (NOT RETVAL) variable method also.
Doing the following is only provided for Visual Basic 6.0 developers to easily port code over:
Public Function MyFunction() As String
MyFunction = "Hello"
End Function
I would definitely not recommend keeping doing it if your project includes anyone who hasn't worked with Visual Basic 6.0, as this syntax will be confusing.
When Tools/Options/Text Editor/All Languages/Code Lens is activated, the Reference Count shows above each Sub, Function, or Property statement.
"Return Value" seems better than "assigning a value to the Functions Name". In the latter case, "Code Lens" produces an inflated Reference Count.
' Code Lens reports "0 references" here for Sub Rosa().
Public Sub Rosa()
Diagnostics.Debug.WriteLine(Test())
Diagnostics.Debug.WriteLine(Test2())
End Sub
' Code Lens reports "2 references" here for Function Test().
Public Function Test() As String
Test = "Test" ' Code Lens counts this as a reference.
End Function
' Code Lens reports "1 reference" here for Function Test2().
Public Function Test2() As String
Dim strTest2 as String = "Test"
Return strTest2 ' Code Lens does NOT count this as a reference.
End Function
99 times out of 100 I'll use "return value".
Every once in a while I'll have a function where the other type not only allows me to save a variable declaration, but do it in a way that actually significantly clarifies the function. Usually this happens when I would want to name the return value the same as the function anyway, and often these are recursive functions; something about that construct lends it to the implicit return variable. However, that scenario is extremely rare. I don't know if I have any functions using implicit return variables at all in my current project.
Having read that the Return Value syntax was the One True .NET Way Of Doing Things I thought "OK, we'll do it that way then". Then I wrote a function which I knew, hand on heart KNEW, returned either a value from a Return statement or alternatively an Exception under all circumstances, and still got a compiler warning that the function "doesn't return a value on all paths".
Thankfully I came across the Stack Overflow question How can I make this function not generate a “doesn't return a value on all paths” warning? which explained why; adding a default value assignment to the procedure name at the head of the function prevented the warning in my case as well.
Consequently, even though I'll continue to use the Return Value syntax simply for the sake of syntax consistency, I'll also be assigning a default value to the function name just to prevent the possibility of cluttering up the compile process with bogus warnings.
its quite handy when working with 3rd party factories(_hsf), you can avoid declaring return variables
Public Function CreateExtremum(iShape As INFITF.Reference, iDir1 As HybridShapeTypeLib.HybridShapeDirection, iSide1 As Integer, iDir2 As HybridShapeTypeLib.HybridShapeDirection, iSide2 As Integer, iDir3 As HybridShapeTypeLib.HybridShapeDirection, iSide3 As Integer) As HybridShapeTypeLib.HybridShapeExtremum
CreateExtremum = _hsf.AddNewExtremum(iShape, iDir1, iSide1)
CreateExtremum.Direction2 = iDir2
CreateExtremum.ExtremumType2 = iSide2
CreateExtremum.Direction3 = iDir3
CreateExtremum.ExtremumType3 = iSide3
CreateExtremum.Compute()
End Function