Replies: 14 comments
-
Sounds like a great place to start. Here's a couple of questions: First, how important is thread safety? That's a big difference between Second (and this might go into bikeshedding territory), why one function instead of two? (Although, honestly, I would almost suggest three: a |
Beta Was this translation helpful? Give feedback.
-
Wanted to leave a quick comment here letting you know that I'm currently digesting these thoughts and goingt o explore CompareExchange further... With that, I do have a quick follow-up question... Three? Wouldn't Inc/Dec be the same as PostInc/PostDec - or am I missing a set of scenarios? |
Beta Was this translation helpful? Give feedback.
-
Really, we don't need the Inc/Dec I suggested. I just like it because it separates "I need the old value", "I need the new value", and "I don't care because I'm just using this as a statement"; and it doesn't force the "I don't care" users to pick PreX or PostX. For the translation from C# scenario, it's probably unnecessary, but I thought I'd toss it in for completeness' sake. |
Beta Was this translation helpful? Give feedback.
-
Did a little code tinkering today, and here's a couple of methods. (I picked different names again, because naming is hard, and I kept forgetting which is which when it comes to prefix and postfix ++, let alone the method names I just posted a couple of days ago.) <Extension> Public Function IncAndGet(ByRef value As Integer) As Integer
value += 1
Return value
End Function
<Extension> Public Function IncAndGet_ThreadSafe(ByRef value As Integer) As Integer
Return Interlocked.Increment(value)
End Function
<Extension> Public Function IncAndGet(ByRef value As Single) As Single
value += 1
Return value
End Function
<Extension> Public Function IncAndGet_ThreadSafe(ByRef value As Single) As Single
'Get local copy of value
Dim oldValue = value
'Get incremented value
Dim newValue = oldValue + 1
'Try to update value (fails if another thread changed it since we got oldValue)
Do Until Interlocked.CompareExchange(value, newValue, oldValue) = oldValue
'Get local copy of value again
oldValue = value
'Get incremented value again
newValue = oldValue + 1
Loop
Return newValue
End Function It's been years since I last tinkered with I wonder if making increment/decrement thread-safe here is overkill. As far as I can tell, in C#, ++ and -- aren't thread-safe; that's why the Next, I may look to see what it would take to add an |
Beta Was this translation helpful? Give feedback.
-
I'm not so sure that it's about "thread safety". Something about the Interlocked stuff manages to get things to work "like C#". Meaning that they provide the same behavior and result as the ++ and -- in C#. Using an x++ in a loop uses the current value of x for the task at hand and upon completion of that task then increments it by 1. Take the following code:
The result is: (1) value++ = 1 (2) So you can see that value is 1 for the first printed result, is 1 for the second "result" and 2 for the final one. The value.ToString() occurs during the task at hand and is a 1 and after the task is completed, it is incremented to a 2 thus showing a 2 for the final task... all within the same line. The Interlocked stuff is somehow allowing the same behavior (for a subset of types supported by it). So the goal is to have the "same" code (but in VB) produce the same output...
The IncAndGet that you have above wouldn't produce the same output as the above (which uses the Interlocked stuff). As I said, there's some sort of Voodoo magic happening here... If you utilize the IncAndGet the output would look like: (1) value++ = 2 (2) I was thinking along the same lines and ended up removing all of the overloads since they weren't working as I'd originally thought - but they were very similar to what you wrote. So I'm wondering if the Interlocked.CompareExchange produces the same Voodoo results, will have to explore this more when I have time. |
Beta Was this translation helpful? Give feedback.
-
All of the methods in my last post, including the ones using Interlocked, were for Here are the ones I wrote for <Extension> Public Function GetAndInc(ByRef value As Integer) As Integer
GetAndInc = value
value += 1
End Function
<Extension> Public Function GetAndInc(ByRef value As Single) As Single
GetAndInc = value
value += 1
End Function
<Extension> Public Function GetAndInc_ThreadSafe(ByRef value As Integer) As Integer
GetAndInc_ThreadSafe = value
Interlocked.Increment(value)
End Function
<Extension> Public Function GetAndInc_ThreadSafe(ByRef value As Single) As Single
'Get local copy of value
Dim oldValue = value
'Get incremented value
Dim newValue = oldValue + 1
'Try to update value (if another thread hasn't just changed it)
Do Until Interlocked.CompareExchange(value, newValue, oldValue) = oldValue
'Get local copy of value again
oldValue = value
'Get incremented value again
newValue = oldValue + 1
Loop
Return oldValue
End Function So, int value = 1;
System.Console.WriteLine($"({value}) value++ = {value++} ({value})"); and Dim value = 1
Console.WriteLine($"({value}) value++ = {value.GetAndInc} ({value})") should both result in (1) value++ = 1 (2) while int value = 1;
System.Console.WriteLine($"({value}) ++value = {++value} ({value})"); and Dim value = 1
Console.WriteLine($"({value}) ++value = {value.IncAndGet} ({value})") should both result in (1) ++value = 2 (2) |
Beta Was this translation helpful? Give feedback.
-
This is awesome! I'll update what I've done to utilize this approach. |
Beta Was this translation helpful? Give feedback.
-
Just added all of this to my current implementation... so this concludes 80% of the "project". Now begins the last 20% of the project which will take 80% of the "effort". ;-) Major questions:
Any other potential questions that I'm not currently thinking of? |
Beta Was this translation helpful? Give feedback.
-
"AVOID frivolously defining extension methods, especially on types outside your library or framework. If you do own source code of a type, consider using regular instance methods instead. If you don't own the type, but you want to add a method, be very careful. Liberal use of extension methods has the potential to clutter APIs with types that were not designed to have these methods. Another thing to consider is that extension methods are a compile-time facility, and not all languages provide support for them. Callers using languages without extension method support will have to use the regular static method call syntax to call your extension methods. There are, of course, scenarios in which extension methods should be employed. These are outlined in the guidelines that follow." -- Framework Design Guidelines 3rd Edition Page 184. The following recommendation seem to mainly be about interfaces, generics and/or functionality that would be in a separate assembly due to assembly dependencies. Finally, one of the big issues as I see regarding extension methods for this type of thing is: "AVOID defining extension methods on System.Object. VB users will not be able to call such methods on object references using the extension method syntax, because VB does not support calling such methods. In VB, declaring a reference as Object forces all method invocations on it to be late bound (the actual member called is determined at runtime), while bindings to extension methods are determined as compile-time (early bound). For example:
VB users will have to call the method using the regular static method call syntax.
Note that this guideline applies to other languages where the same binding behavior is present, or where extension methods are not supported." -- Page 188 There is another annotated comment from Anthony Moore that discusses how overusing of extension methods reminds him of a similar problem experienced in C++. So it would appear to me that the recommendation is that these shouldn't be extension methods. Thoughts? |
Beta Was this translation helpful? Give feedback.
-
As for naming, I'm leaning to the following:
Part of the logic behind this is that I'd like to keep the methods as small/concise as possible so that (hopefully) the code will be inlined when compiled. The "Inline" portion may be something that we adjust depending on whether or not it is in it's own namespace ("???.Inline"). So we could have the following:
I'm 50/50 on whether or not the Increment methods should have the word After or not. With that said, I think I'm leaning more to having them state After then not... but it's a very thin lean as I think that the most common behavior is value++ and/or value--. |
Beta Was this translation helpful? Give feedback.
-
I think most of those questions could each almost be a big repo-wide design issue on its own. Microsoft.VisualBasic doesn't, as I recall, contain any extension methods, so these don't have to be extension methods, either. Also, it's easier and less breaking to add As for where to put it, if these methods are meant for general use, we could put increment and decrement in the existing VBMath module, and assignment might fit in the existing Interaction module (Why? Because IIf and Switch are in there.) If these methods are mainly for converting C# code to VB, we could take a page from the Microsoft.VisualBasic.Compatibility.VB6 namespace and create a Community.VisualBasic.Compatibility.CSharp namespace and put an Inline module in there. As for naming, while I suspect Also, I really don't have a strong opinion on |
Beta Was this translation helpful? Give feedback.
-
What about types that overload op_Increment and op_Decrement? |
Beta Was this translation helpful? Give feedback.
-
@Echo-8-ERA Do you have an example? |
Beta Was this translation helpful? Give feedback.
-
I once wrote a fixed point math library which overloaded op_increment/decrement to add/subtract 1. Lost the code when I changed my OS. |
Beta Was this translation helpful? Give feedback.
-
See dotnet/vblang#231
My thinking is that the following C# code...
int value = 1;
Console.WriteLine($"({value}) value=5 = {value=5} ({value})");
Console.WriteLine($"({value}) value++ = {value++} ({value})");
Console.WriteLine($"({value}) value-- = {value--} ({value})");
Console.WriteLine($"({value}) ++value = {++value} ({value})");
Console.WriteLine($"({value}) --value = {--value} ({value})");
converted to VB as...
Dim value As Integer = 1
Console.WriteLine($"({value}) value=5 = {value.Assign(5)} ({value})")
Console.WriteLine($"({value}) value++ = {value.Incr} ({value})")
Console.WriteLine($"({value}) value-- = {value.Decr} ({value})")
Console.WriteLine($"({value}) ++value = {value.Incr(Apply.Before)} ({value})")
Console.WriteLine($"({value}) --value = {value.Decr(Apply.Before)} ({value})")
This works great for Integer, UInteger, Long and ULong... but the other types can't use the same "trick". More thinking needs to be done to determine if there is method where the same sort of code pattern can work on Byte, SByte, Char, Short, UShort, Single, Double, etc.
Example.txt
Beta Was this translation helpful? Give feedback.
All reactions