I have a simple code in C#:
Console.WriteLine(string.Join<char>("", ""));
And I can't convert it to VB.Net. Even reflector show me code in VB like:
Console.WriteLine(String.Join(Of Char)("", ""))
But it can't be compiled becouse I have an starge error:
Error 1 Expression expected.
It looks like VB.Net don't have this generic method at all.
Both project use Net Framework 4.
Why this error happened?
UPD:
I've create a custom class and copy Join(Of T) declaration to it:
Class string2
Public Shared Function Join(Of T)(ByVal separator As String, ByVal values As System.Collections.Generic.IEnumerable(Of T)) As String
Return "1"
End Function
End Class
Console.WriteLine(string2.Join(Of Char)("", ""))
It works
UPD2:
My compilation string, where you can see that I'm using Net4:
http://pastebin.com/TYgS3Ys3
Do you have a code element named String somewhere in your project?
Based on the answer you have added to this question (where you indicate that changing String to [String] appears to have solved the problem), I guessed that this may be the result of a naming collision.
I was able to duplicate the error you are seeing -- "Expression expected" -- by adding a module to my project called String and defining a (non-generic) Join method from within that module.
This may not be the specific scenario you find yourself in. But the fact that the code works for you with [String] is, to me, very compelling evidence of a simple namespace collision.
Based on the documentation for the "Expression expected" error, I'm guessing you haven't included the entire section of code where this error is appearing for you.
Do you have a lingering operator such as + or = somewhere?
(The VB.NET code you posted is indeed equivalent to the C# code above it and should compile no problem. This is why I suspect the real issue lies elsewhere.)
String.Join<T>(string, IEnumerable<T>) is useful with LINQ, for standard joins is better to use the String.Join(string, string()) overload.
In C#, "" as Char produces an empty Char (\0). Writing the same thing ("") in VB produces an empty string which is not the same as an empty char. In order to produce an empty character, you'll have to write New Char().
Your VB code therefore becomes:
Console.WriteLine(String.Join(Of Char)(New Char(), New Char()))
Edit
I just checked and it appears String.Join does not support the format you're specifying.
Instead, it goes as follows:
Join(separator As String, value As String()) As String
Your code should be as follows:
Console.WriteLine(String.Join("", New String() {""}))
String.Join(Of Char)(str1, str2) wasn't added til .net 4, it seems. That's why your custom class worked -- it had the declaration, but the String class in the framework you're actually using doesn't.
Check your settings and references to make sure you're targeting .net 4 all around -- cause that's the only thing that seems able at this point to stop the call from working.
Here the solution:
Console.WriteLine([String].Join(Of Char)("", ""))
Why this problem occurs only with generic method? I wish I know...
Related
I am trying to get a VB.NET app to compile. Besides the "elephant in the room", I'm also getting 7 "'Trim' is not declared" errors on code like this:
...as well as one "'IsNothing' is not declared. It may be inaccessible due to its protection level." on this line:
If IsNothing(memberList) = False Then
I don't know VB, so there may be a simple solution to this, but I have no clue what the problems are.
The Trim function requires a reference to Microsoft.VisualBasic from the assembly Visual Basic Runtime Library (in Microsoft.VisualBasic.dll)
Usually is preferable to use the native Trim method from the string class and not add a reference to this assembly (mainly used to help porting old VB6 apps)
mail.CC.Add(addr.Trim())
Notice also that the string.Trim removes other whitespace characters as tabs while the Microsoft.VisualBasic function does not.
You have to use addr.Trim instead of Trim(addr)
Read more about Trim in this MSDN article
And you should use
If not memberList Is Nothing Then
Instead of
If IsNothing(memberList) = False Then
Or
You have to import Microsoft.VisualBasic namespace
If you use the Left(), Mid(), and Right() string functions you might find it easier to convert those too:
Left(t, l) becomes t.Substring(0, l)
Mid(t, s, l) becomes t.Substring(s-1, l)
Right(t, l) becomes t.Substring(t.Length - l)
Often Left and Right are properties and stop you using the old VB string functions.
String.Trim doesn't take a string parameter. It returns a new string in which all leading and trailing occurrences of a set of specified characters from the current String object are removed.
It should be...
addr.Trim()
Is there any difference under the hood between line 4 and line 5?
Why can't VB.net handle Line 3?
What is the proper way to call the function?
Dim aFunc As New Tuple(Of Func(Of String))(Function() "Hello World")
Dim s As String
s = aFunc.Item1() 'does not compile
s = (aFunc.Item1)()
s = aFunc.Item1.Invoke()
This looks like a compiler bug to me, the parentheses should make it unambiguously a method call. Hard to state this for a fact however, parens are heavily overloaded in vb.net to mean many things. Clearly it is the tuple that makes the compiler fumble, it works fine without it. This came up in this week's StackExchange podcast with Eric Lippert btw, you might want to listen to it to get the laundry list of things it can mean.
You could post this to connect.microsoft.com to get the opinion of the language designers. The behavior is certainly unintuitive enough to call it a bug. The workarounds you found are good. Both generate the exact same code and add no overhead, something you can see by running ildasm.exe on your assembly.
aFunc.Item1 is a Function, so you can't assign it to a String. You appear to want:
Dim aFunc As New Tuple(Of Func(Of String))(Function() "Hello World")
Dim s As String
Dim f As Func(Of String) = aFunc.Item1
s = f.Invoke()
EDIT:
s = aFunc.Item1() accesses the property Item1. To invoke the function which that property refers to, you can use s = aFunc.Item1()(), which is equivalent to your line 4. At a guess, property access is stronger than function invocation (if those are the correct terms).
As a summary: I'm trying to get String.Concat to use a reference type's ToString overload when sticking string together.
Edit: Added this overview: The example code below is a cooked down extract of my real code - as such it would be immediately obvious when refactoring if I only had two lines of code. The important issue here (to me) is that I changed from a string to an object and there was no compile error from String.Concat. Equally it's behaviour wasn't what I would have expected (Using my object's to string method, rather than the bog standard object name). If I'd been using "&", there would have been a compile error. I'm concerned that the String.Concat syntactic sugar may lead to bugs that otherwise would have been avoided (in this case when refactoring). I'd like to know if there's a way of altering the behaviour of String.Concat or if I should consider it to be dangerous.
The situation:I've got a solution which processes a whole heap of data; I was using a String to contain the identifier of each piece of data, but have just swapped this out for a class (FeatureIdentifier) to enable me to extend the identifier to include things like batches etc.
I've refactored my code so that I use this class instead of just the string. When refactoring this type of thing (rightly or wrongly) I tend to rely on compile errors as a to-do list.
Now, I'm a self-taught programmer and I'm probably a bit set in my ways (I tend to look at new features in terms of if they let me do anything new rather than if they let me do stuff I can already do only easier) and I've just come across something which makes me sad.
So, I was sticking my identifier onto an underscore onto a type. My code looked like this:
Dim x as string = "MyIdentifer"
dim myOutputValue as string = String.Concat(x,"_ANCHOR.txt")
Running this I got myOutputValue equal to "MyIdentifier_ANCHOR.txt". Following refactoring, my code looked like this:
Dim x as new FeatureIdentifier("MyIdentifier")
dim myOutputValue as string = String.Concat(x,"_ANCHOR.txt")
Running this I got myOutputValue equal to "MyNamespace.FeatureIdentifier_ANCHOR.txt".
Having kicked myself and implemented a ToString method on my class, I run it again and get exactly the same output (that is "MyNamespace.FeatureIdentifier_ANCHOR.txt"). In immediate, if I do: ?x.ToString, I get "MyIdentifier", so I'm certain I've implemented ToString correctly.
So, here's my problem. I like the syntax of String.Concat but I don't like the fact that it doesn't do one of:
a) calling ToString on reference types it sticks together orb) throwing a compile error if you pass it non-string based arguments. The old school: x & "_ANCHOR.txt" gives me a compile error (which I would have picked up when refactoring).
Here's what I've tried:
I've tried shadowing the String.Concat function with an extension (something like this:
<System.Runtime.CompilerServices.Extension()> Public Function Concat(...some arguments...) As String
Return String.Concat(...some arguments...)
End Function
) but hit two problems:
1) When trying to narrow the type of the arguments down to string to cause compile errors, I realised that the argument is a param array and hence objects in the first place. So fail there.
2) When I tried to make multiple overloads ((s1 as string, s2 as string), (s1 as string, s2 as string, s3 as string) etc), I felt it was a bit lame and also discovered that you can't actually overload an extension on a static class (which is what I guess String is).
So, does anyone know a way of getting String.Concat to behave as well as old-school concatenation, or should I avoid String.Concat in favour of old-school concatenation?.
(I'm not going to use a StringBuilder, as I'm only concatenating a few strings and I don't believe this is the place for one).
I don't believe there is any way that you are going to "fix" the String.Concat method to only allow strings. Chalk it up to another reason why shared methods should be created and used as sparingly as possible. However, through the miracle of operator overloading, you can make your custom class work just like a string. To fix the Concat method, you need to overload the CType operator. To fix the string concatenation operator (&), you need to overload that operator separately, like this:
Public Class FeatureIdentifier
Public Sub New(id As String)
Me.Id = id
End Sub
Public Property Id As String
Public Property SomethingElse As Integer
Public Overloads Shared Widening Operator CType(value As FeatureIdentifier) As String
Return value.Id
End Operator
Public Overloads Shared Operator &(value1 As FeatureIdentifier, value2 As String) As String
Return value1.Id & value2
End Operator
Public Overloads Shared Operator &(value1 As String, value2 As FeatureIdentifier) As String
Return value1 & value2.Id
End Operator
End Class
Now you can use it like this:
Dim x As New FeatureIdentifier("MyIdentifier")
Dim myOutputValue As String = String.Concat(x, "_ANCHOR.TXT")
Or like this:
Dim x As New FeatureIdentifier("MyIdentifier")
Dim myOutputValue As String = x & "_ANCHOR.TXT"
And it will work just like as if it were still a string, in those circumstances. You may also want to overload some of the other operators too, just in case. For instance, the + operator also concatenates when applied to two strings. However, I should caution you that operator overloading can cause confusion to people who are not familiar with the code, since it works unexpectedly, so you should only use it if it really makes sense to do so.
Do you need to override ToString and even make sure it's called for this? It would seem to be more proper to expose a property of FeatureIdentifier named Identifier, or some such thing, and then you can just do:
Dim myOutputValue as string = String.Concat(x.Identifier, "_ANCHOR.txt")
I'm having trouble with a .NET Assembly that is com visible, and calling certain methods from VB6.
What I have found is that if the parameters are well defined types, (e.g. string), calls work fine. If they are higher level objects, it raises a runtime error '438' suggesting that the property or method is not present. I suspect that this is a question of having the correct signature on the call, but I can't see how to do this correctly.
I believe that I've done everything correct on the .NET side (ComVisible, public interfaces, etc. and even have it down to a simple enough case).
Looking at the output from the typelib viewer, I have the following:
dispinterface ISimple {
properties:
methods:
[id(0x60020000)]
void Add([in] ISimpleMember* member);
[id(0x60020001)]
ISimpleMember* Create();
};
OK. So I have 2 methods in my ISimple interface. One takes an ISimpleMember (Add), whilst the other, returns an ISimpleMember.
The corresponding code in VB looks like this:
Dim item As ISimpleMember
Dim simple As simple
Set item = New SimpleMember
item.S1 = "Hello"
item.S2 = "World"
Set simple = New simple
simple.Add (item) <---- This raised the run time error 438
Set item = simple.Create <---- This works fine, returning me an ISimpleMember
I've tried a couple of things:
1. Dim item as SimpleMember (makes no difference)
2. simple.Add(ObjPtr(item)) - Syntax error
3. simple.Add(ByRef item) - Syntax error
Basically, The run time error is the same as if I had
simple.AMethodThatIHaventWritten()
Also, If I browse References in the VB6 Environment, The Add method is well defined:
Sub Add(member As SimpleMember)
I've found the answer I believe. It was very simple:
When calling a SubRoutine, I shouldn't put the name in braces. the call should have been:
simple.add member
rather than
simple.add(member)
If I change it to a function (i.e. return a value rather than void) the braces are necessary
This seems to work
(Probably) The top 3 VB6 coding mistakes made by devs who now mainly code in C#, Javascript etc. Are:-
Placing ; at the end of lines. Its a syntax error very easily spotted and picked up the compiler.
Not placing Then on the other side of an If condition expression. Again its a syntax error.
Calling a method without retrieving a value and yet using ( ) to enclose the parameter list. With multiple parameters this is a syntax error and easily found. With only one parameter the use of ( ) is interpreted as an expression. Its the result of the ( ) expression which is passed as parameter. This causes problems when ByRef is expected by the callee.
I'm calling a function that returns a string, but it's only actually returning the first character of the string it's supposed to be returning.
Here's a sample piece of code to recreate the issue I'm experiencing:
Public Function GetSomeStringValue(Value as Integer) As String
... Code Goes here
Return Some_Multicharacter_string
End Function
The function call looks like:
SomeStringValue = GetSomeStringValue(Value)
Why is this not returning the entire string?
Note: this answer was originally written by the OP, Kibbee, as a self-answer. However, it was written in the body of the question, not as an actual separate answer. Since the OP has refused repeated requests by other users, including a moderator, to repost in accordance with site rules, I'm reposting it myself.
After trying a hundred different things, refactoring my code, stepping through the code in the debugger many times, and even having a co-worker look into the problem, I finally, in a flash of genius, discovered the answer.
At some point when I was refactoring the code, I changed the function to get rid of the Value parameter, leaving it as follows:
Public Function GetSomeStringValue() As String
... Code Goes here
Return Some_Multicharacter_String
End Function
However, I neglected to remove the parameter that I was passing in when calling the function:
SomeStringValue = GetSomeStringValue(Value)
The compiler didn't complain because it interpreted what I was doing as calling the function without brackets, which is a legacy feature from the VB6 days. Then, the Value parameter transformed into the array index of the string (aka character array) that was returned from the function.
So I removed the parameter, and everything worked fine:
SomeStringValue = GetSomeStringValue()
I'm posting this so that other people will recognize the problem when/if they ever encounter it, and are able to solve it much more quickly than I did. It took quite a while for me to solve, and I hope I can save others some time.