Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
underage at the vape shop
May 11, 2011

by Cyrano4747
I fixed the bugs and it passes all the tests but is the most correct way of doing this? mapFold doesnt even exist in Microsofts documentation

Adbot
ADBOT LOVES YOU

Mr Shiny Pants
Nov 12, 2012
You should have pasted text :)

You could calculate the moves and do the perspective check at the end to determine if you need the min or the max from the sequence. That way you can delete a whole lot of stuff from the function.

I am not a great programmer, but they way I look at it is that if you have multiple lines doing the same stuff you could probably remove them and put them in one function and have the logic that makes it functionally different at the end or you pass it in as a parameter.

Something like this:

code:

let rec miniMax game perspective =
    match gameOver game with
    | true -> (None, heuristic game perspective)
    | false ->
        let result = 
            let moves....
            //other stuff
        match perspective with
        | true -> 
            result
            |> Seq.maxBy snd
        | false ->
            result
            |> Seq.minBy snd
As I said, I did not check it completely so maybe I am missing something obvious. I hope it makes sense.

This is just going from your existing code, there are some really smart ways of working with trees in F#, so maybe if you take a step back there is probably a really clever way of doing this.

Mr Shiny Pants fucked around with this message at 11:15 on Mar 31, 2019

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

New mandatory dependency for all new and legacy projects (but especially legacy):

https://github.com/aaronpowell/DisappointinglyAttributed/

LongSack
Jan 17, 2003

Question on HashSet<T> and custom classes. I am working on a program to help automate the creation of firewall rules from a spreadsheet submitted by the user. As part of that process, I am trying to match or find the longest match for the list of ports requested from the object groups on the firewalls. To do this, i have a class that encodes a protocol entry in an object group:
C# code:
public class ProtocolEntry : IEquatable<ProtocolEntry>
{
    public Protocol Protocol { get; private set; }
    public int Port { get; private set; }
    public int EndPort { get; private set; }
}
(Protocol is an enum) so if an object group contains say ports TCP/80, TCP/443 and TCP/8080-8089, there would be 3 ProtocolEntry objects in the ObjectGroup object.

To determine whether object-group a is a subset of object-group b, I had the following:
C# code:
public class ProtocolEntry : IEquatable<ProtocolEntry>
{
    public IEnumerable<int> PortNumbers()
    {
        if (EndPort == 0)
        {
            yield return Port;
        }
        else
        {
            for (int p = Port; p <= EndPort; p++)
            {
                yield return p;
            }
        }
    }
}

public static class Tools
{
    public static IEnumerable<int> Ports(this IEnumerable<ProtocolEntry> entries)
    {
        List<int> ret = new List<int>();
        foreach (var entry in entries)
        {
            ret.AddRange(entry.PortNumbers());
        }
        return ret;
    }

    public static bool IsSubsetOf(this IEnumerable<ProtocolEntry> list1, IEnumerable<ProtocolEntry> list2)
    {
        var p1 = new HashSet<int>(list1.Ports());
        var p2 = new HashSet<int>(list2.Ports());
        return p1.IsSubsetOf(p2);
    }
}
This worked, except that it didn't take into account the Protocol, so in testing it matched a UDP object group to a list of requested TCP ports. That won't work.

As a fix, i changed the ProtocolEntry.PortNumbers() routine to return a list of ((int)Protocol << 16) | Port. Now it works and accounts for Protocol, but it feels clumsy.

I tried using HashSet<ProtocolEntry>, but the HashSet.IsSubsetOf routine did not work, always returning false. Is there something special I need to do to my classes to make them work in this instance? I have the equality methods:
C# code:
public override bool Equals(object obj)
{
    if (!(obj is ProtocolEntry p))
        return false;
    return p.Protocol == Protocol && p.Port == Port && p.EndPort == EndPort;
}

public bool Equals(ProtocolEntry p) => p == null ? false : p.Protocol == Protocol && p.Port == Port && p.EndPort == EndPort;

public override int GetHashCode()
{
    var hashCode = -532373600;
    hashCode = hashCode * -1521134295 + Protocol.GetHashCode();
    hashCode = hashCode * -1521134295 + Port.GetHashCode();
    hashCode = hashCode * -1521134295 + EndPort.GetHashCode();
    return hashCode;
}
(The GetHashCode() method was generated by Visual Studio)

Thanks in advance.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

The code is fine, the logic is wrong.

If you compare the sets of individual port numbers, TCP 80-100 is a subset of TCP 60-120.

If you compare the sets of protocol entries, you have two sets each with one item, and the two items are different.

Go back to comparing the lists of individual ports, and just fix PortNumbers() so that it returns a list of tuples of port type and number. Or split it into two methods, TCPPortNumbers and UDPPortNumbers, and check both lists separately. Or return a Dictionary<PortType, int[]> and check every key in both dictionaries.

NihilCredo fucked around with this message at 19:31 on Apr 1, 2019

LongSack
Jan 17, 2003

NihilCredo posted:

The code is fine, the logic is wrong.

If you compare the sets of individual port numbers, TCP 80-100 is a subset of TCP 60-120.

If you compare the sets of protocol entries, you have two sets each with one item, and the two items are different.

Go back to comparing the lists of individual ports, and just fix PortNumbers() so that it returns a list of tuples of port type and number. Or split it into two methods, TCPPortNumbers and UDPPortNumbers, and check both lists separately. Or return a Dictionary<PortType, int[]> and check every key in both dictionaries.

The code works fine when I convert a list of ProtocolEntries to a list of ints. The generator method and the extension method will take a list of ports, say TCP/80, TCP/443, TCP/8080-8089 and convert it to a list of ints { 80, 443, 8080, ..., 8089 }. The “fix” just slaps the protocol enum value into the high order word. That now works perfectly, though it feels like (and is, really) a kludge.

What didn’t work is:
C# code:
public static IsSubsetOf(this IEnumerable<ProtocolEntry> list1, IEnumerable<ProtocolEntry> list2)
{
    var p1 = new HashSet<ProtocolEntry>(list1);
    var p2 = new HashSet<ProtocolEntry>(list2);
    return p1.IsSubsetOf(p2);
}
Even though ProtocolEntry implements equality and GetHashCode() should return the same value for objects with the same Protocol, Port and EndPort.

So I was curious if there was something I needed to implement to make the above code work.

Jabor
Jul 16, 2010

#1 Loser at SpaceChem
Suppose you have a ProtocolEntry x, which represents tcp ports 60-120. And you have another ProtocolEntry y, which represents tcp ports 80-100.

The issue is that your HashSet has no way of knowing that y represents a "subset" of x - all it knows is that the ProtocolEntrys are not equal, so neither HashSet is a subset of the other.

If you want a slightly-less-kludgy version of your current solution, you can do as Nihil Credo suggests - use a helper method that, instead of returning a port number (that's only meaningful if you already know the protocol), returns a tuple of <protocol, port number>. Then you can stick those in a set and subset it.

If you want a cleaner solution ... fundamentally, with your current api, the code doing the comparison has absolutely no way to tell that a particular protocol entry represents a range of ports with defined endpoints. The only way that it can look at a ProtocolEntry is to iterate over all the individual ports that the ProtocolEntry contains. So you're going to have to expand the api with this particular use case in mind.

Shy
Mar 20, 2010

New Visual Studio :woop: what does it do?

LongSack
Jan 17, 2003

Jabor posted:

Suppose you have a ProtocolEntry x, which represents tcp ports 60-120. And you have another ProtocolEntry y, which represents tcp ports 80-100.

The issue is that your HashSet has no way of knowing that y represents a "subset" of x - all it knows is that the ProtocolEntrys are not equal, so neither HashSet is a subset of the other.

If you want a slightly-less-kludgy version of your current solution, you can do as Nihil Credo suggests - use a helper method that, instead of returning a port number (that's only meaningful if you already know the protocol), returns a tuple of <protocol, port number>. Then you can stick those in a set and subset it.

If you want a cleaner solution ... fundamentally, with your current api, the code doing the comparison has absolutely no way to tell that a particular protocol entry represents a range of ports with defined endpoints. The only way that it can look at a ProtocolEntry is to iterate over all the individual ports that the ProtocolEntry contains. So you're going to have to expand the api with this particular use case in mind.

Yeah, returning a collection of Tuple<Protocol, int> is less of a kludge and is the same functionally as ((int)Protocol << 16) | Port. It works, too. Thanks, and thanks to Nihil Credo.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Shy posted:

New Visual Studio :woop: what does it do?

Big one is real-time collaborative editing and debugging that, as far as we could try, actually works and is really good.

Other interesting novelties are a nice vertical-space-saving title bar and random Intellisense slowdowns and freezes during regular use.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



NihilCredo posted:

random Intellisense slowdowns and freezes during regular use.

M$ ripping off more R# features :argh:

Scaramouche
Mar 26, 2001

SPACE FACE! SPACE FACE!

Might check it out for the c# 8 preview stuff that I missed in December:
- Null warnings on specified reference types
- Range/index slicing in arrays
- Asynch IEnumerable<T>

SixPabst
Oct 24, 2006

code:
public static int Foo()
{
    const int returnCode = 0;

    switch (returnCode)
    {
        case 0:
        return returnCode;
    }
}
This will compile successfully in VS2017 but not in 2019. :v:

Captain Capacitor
Jan 21, 2008

The code you say?
Also F# got anonymous records.

raminasi
Jan 25, 2005

a last drink with no ice

SixPabst posted:

code:
public static int Foo()
{
    const int returnCode = 0;

    switch (returnCode)
    {
        case 0:
        return returnCode;
    }
}
This will compile successfully in VS2017 but not in 2019. :v:

What’s the error?

SixPabst
Oct 24, 2006

raminasi posted:

What’s the error?

Not all code paths return a value.

I think in 17 it was smart enough to see that there's really only one execution path and removes the switch

LongSack
Jan 17, 2003

Scaramouche posted:

Might check it out for the c# 8 preview stuff that I missed in December:
- Asynch IEnumerable<T>

How can a class be async? What am I missing here?

edit: Oh, wait is it enumerators?

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings

LongSack posted:

How can a class be async? What am I missing here?

edit: Oh, wait is it enumerators?

It's effectively being capable of doing:

await foreach(var result from GetThings())

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



SixPabst posted:

Not all code paths return a value.

I think in 17 it was smart enough to see that there's really only one execution path and removes the switch

Wonder if that's related to new language features around pattern matching? Or were those all present in C# 7?

Not that it makes sense, but switch finally got some useful features nailed onto it so it's like a dusty basement roomfloor of the compiler is getting remodeled for the first time so stuff is gonna break.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Munkeymon posted:

Wonder if that's related to new language features around pattern matching? Or were those all present in C# 7?

Not that it makes sense, but switch finally got some useful features nailed onto it so it's like a dusty basement roomfloor of the compiler is getting remodeled for the first time so stuff is gonna break.

The C# compiler team is very good about not introducing breaking changes in general, so it's always noteworthy when they do, whether intentional or by mistake.

LongSack
Jan 17, 2003

Question about LINQ.

Cisco firewalls have service object groups which consist of a collection of entries which can be a single port, a range of ports, or another object group. In my firewall modeling app, my ServiceObjectGroup class has a GetPorts() method that returns a IEnumerable<int> that is the complete collection of port numbers in the group.

The class looks like this (stuff redacted):
C# code:
public class ServiceObjectGroup
{
    public Protocol Protocol { get; set; }
    public List<ServiceObjectEntry> Entries { get; set; }
}
Most service object groups also are a single IP protocol (TCP, UDP, ICMP, etc.).

I wanted to add a method which returned an IEnumerable<(Protocol, int)>.

I couldn’t figure out how to do this with LINQ. This failed:
C# code:
public IEnumerable<(Protocol, int)> Ports() => from p in GetPorts() select new (Protocol, p);
I ended up doing a foreach loop:
C# code:
public IEnumerable<(Protocol, int)> Ports()
{
    List<(Protocol, int)> ret = new List<(Protocol, int)>();
    foreach (int port in GetPorts())
    {
        ret.Add((Protocol, port));
    }
    return ret;
}
Is there a way to make the LINQ version work? I realize that the Protocol is not captured in the LINQ variable, so is there a way to do it? TIA

LongSack fucked around with this message at 04:56 on Apr 5, 2019

NiceAaron
Oct 19, 2003

Devote your hearts to the cause~

You didn't share the compiler error but I'm guessing the problem is the unnecessary "new" operator.

brap
Aug 23, 2004

Grimey Drawer

SixPabst posted:

code:
public static int Foo()
{
    const int returnCode = 0;

    switch (returnCode)
    {
        case 0:
        return returnCode;
    }
}
This will compile successfully in VS2017 but not in 2019. :v:

Seems related to https://github.com/dotnet/roslyn/issues/33783 which should ship in VS2019 Update 1.

LongSack
Jan 17, 2003

NiceAaron posted:

You didn't share the compiler error but I'm guessing the problem is the unnecessary "new" operator.

The “new” operator is used to create some new class from a LINQ expression. The idea is to take an IEnumerable<int> and produce an IEnumerable<(Protocol, int)>. select new ... is the normal way to do that with LINQ, but since the Protocol is not captured in the LINQ expression, I couldn’t figure out how to do that.

NiceAaron
Oct 19, 2003

Devote your hearts to the cause~

LongSack posted:

The “new” operator is used to create some new class from a LINQ expression. The idea is to take an IEnumerable<int> and produce an IEnumerable<(Protocol, int)>. select new ... is the normal way to do that with LINQ, but since the Protocol is not captured in the LINQ expression, I couldn’t figure out how to do that.

The "new" operator doesn't have any special meaning in a LINQ expression, it does the same thing that it does outside of a LINQ expression. You don't need the "new" operator to create a tuple. Compare with this line in your foreach loop:

code:
ret.Add((Protocol, port));
where you successfully create a tuple without using the "new" operator.

brap
Aug 23, 2004

Grimey Drawer
Yeah, sounds like you may have mixed up anonymous types and the new tuples feature.

SixPabst
Oct 24, 2006

brap posted:

Seems related to https://github.com/dotnet/roslyn/issues/33783 which should ship in VS2019 Update 1.

Yep, that sure seems like the same thing. Thanks!

LongSack
Jan 17, 2003

brap posted:

Yeah, sounds like you may have mixed up anonymous types and the new tuples feature.

I did indeed.

LongSack
Jan 17, 2003

NiceAaron posted:

You didn't share the compiler error but I'm guessing the problem is the unnecessary "new" operator.

And you were absolutely correct. Taking the "new" out fixed it. The error message should have been a clue if i had bothered to read it all :doh:
code:
"'new' cannot be used with tuple type. Use a tuple literal expression instead"

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Thinking about it, I can't come up with a meaningful use case for anonymous types now that C# has named tuples.

The best I can think of is a sort of anti-feature - anonymous types are restricted to the local scope (can't be a return type, property, field, function parameter, etc.), so you can't be tempted to be lazy and just return a giant tuple instead of defining an actual type with a name.

LongSack
Jan 17, 2003

NihilCredo posted:

Thinking about it, I can't come up with a meaningful use case for anonymous types now that C# has named tuples.

The best I can think of is a sort of anti-feature - anonymous types are restricted to the local scope (can't be a return type, property, field, function parameter, etc.), so you can't be tempted to be lazy and just return a giant tuple instead of defining an actual type with a name.

The only place I see is in EF Fluent configuration to designate multi-column keys. Not sure if a tuple would work there.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I am hoping to intercept awaiters and do some custom scheduling for a subsection of my code. The sense I get is I would wrap the invocations in tasks and manually extract awaiters out of them. However, the .NET documentation for tasks sounds like they'll fire off in other threads. Is that the norm? I want to stay on one thread here and I'm alternating through this stuff sequentially.

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



NihilCredo posted:

Thinking about it, I can't come up with a meaningful use case for anonymous types now that C# has named tuples.

The best I can think of is a sort of anti-feature - anonymous types are restricted to the local scope (can't be a return type, property, field, function parameter, etc.), so you can't be tempted to be lazy and just return a giant tuple instead of defining an actual type with a name.

If you're going to have NewtonSoft.Json serialize whatever object graph you're making, you'll want to use anonymous objects unless they change the way they handle tuples by default.

ljw1004
Jan 18, 2005

rum

Rocko Bonaparte posted:

I am hoping to intercept awaiters and do some custom scheduling for a subsection of my code. The sense I get is I would wrap the invocations in tasks and manually extract awaiters out of them. However, the .NET documentation for tasks sounds like they'll fire off in other threads. Is that the norm? I want to stay on one thread here and I'm alternating through this stuff sequentially.

It's certainly possible to intercept awaiters, and I've done that lots. However "wrap the invocation in a task" doesn't mean anything. Do you mean "Task.Run(lambda that performs invocation)"? that's the wrong approach, and will indeed use a thread.

I wrote a bunch of stuff about custom awaiters, and how the compiler expands out the "await" statement. If you read that you'll see that you get complete control over everything, and this will unlock what you need. https://web.archive.org/web/20171014234653/https://blogs.msdn.microsoft.com/lucian/2012/12/11/how-to-write-a-custom-awaiter/

One idiom you might decide to settle on is "await MyAsyncMethod().WithCustomAwaiter()" where you tell the compiler to use your custom machinery at the invocation site.

(There was a new feature in C# in 2017 or so which also lets you declare your own tasklike type where you tell the compiler to use your custom machinery at the declaration site, "async MyCustomTasklike<int> FooAsync() {...}".)

I've used these techniques ("custom scheduling") to [1] write something that "hibernates" an async method to disk so it can be reloaded upon a future invocation and resume execution from where it left off, and [2] write a probabilistic programming language within C# so that when you do "var x = await RollDice()" then the program execution continues six times for each different value of x.


Another idea, depending on what you need, is to replace your current SynchronizationContext. The normal Task and Task.GetAwaiter() just defer to the current SynchronizationContext to decide how to schedule the work.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
I didn't expect such a good response!

ljw1004 posted:

It's certainly possible to intercept awaiters, and I've done that lots. However "wrap the invocation in a task" doesn't mean anything. Do you mean "Task.Run(lambda that performs invocation)"? that's the wrong approach, and will indeed use a thread.
That's roughly what I figured from using Tasks. I didn't study them much deeper because the overall documentation on them was very proud about its threading.

quote:

I wrote a bunch of stuff about custom awaiters, and how the compiler expands out the "await" statement. If you read that you'll see that you get complete control over everything, and this will unlock what you need. https://web.archive.org/web/20171014234653/https://blogs.msdn.microsoft.com/lucian/2012/12/11/how-to-write-a-custom-awaiter/

One idiom you might decide to settle on is "await MyAsyncMethod().WithCustomAwaiter()" where you tell the compiler to use your custom machinery at the invocation site.
I'm going to have to chew on that a few times. I have implemented INotifyCompletion, like, once before bit it was awhile ago so that's all fallen out of my head. Something like that idiom might be fine since everything I'm exposing to this is a wrapper of some form or another.

quote:

(There was a new feature in C# in 2017 or so which also lets you declare your own tasklike type where you tell the compiler to use your custom machinery at the declaration site, "async MyCustomTasklike<int> FooAsync() {...}".)
This?
https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-7#generalized-async-return-types. If so, I wouldn't have inferred that from that blurb.

quote:

I've used these techniques ("custom scheduling") to [1] write something that "hibernates" an async method to disk so it can be reloaded upon a future invocation and resume execution from where it left off, and [2] write a probabilistic programming language within C# so that when you do "var x = await RollDice()" then the program execution continues six times for each different value of x.

So this kind of scares the poo poo out of me and I'm curious about it too because I did intend on being able to serialize all my state to disk in the middle of God-knows-what. What I expected to have to do was manually reconstruct all of these awaiters when I reloaded. If I can can just serialize them at will then huzzah.

quote:

Another idea, depending on what you need, is to replace your current SynchronizationContext. The normal Task and Task.GetAwaiter() just defer to the current SynchronizationContext to decide how to schedule the work.
I'm intending to run all this inside Unity, which I imagine is doing something with the SynchronizationContext already. Still, it's very tempting to just hijack that, save off the current context, intercept it for my stuff, and dump whatever isn't my stuff off to what used to be the current context. It makes me feel like I just discovered daddy's gun and something bad will happen.

I guess I can explain the dumb thing I'm doing. I'm messing around with a Python interpreter with microthreading; I couldn't get IronPython to do what I needed to do, so I started doodling my own. I had a trial coroutine opcode that was working just fine when everything was all Python. However, it kind of went to hell when I had, say, Python code call C# code call Python code. That kind of jump was necessary to implement class declarations in a way similar to CPython, by the way. If the class body had something in it that would cause it to pause, everything would go to hell because I didn't have a means to have the C# code sitting above it to stop its frame and reschedule something else to run.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
I'm creating a new app to replace a horrible old VB.NET app that hasn't been touched for years. This is involving a certain amount of reading the code and also some reverse engineering. I don't know VB from a hole in the ground.

At one point in the code, the VB app calls a method that accepts a String parameter and passes in a Double. Specifically:

Sub XmlTextWriter.WriteString(Text As String)

is being passed the return value of

Function MyClass.MyMethod() As Double

I think this is equivalent to calling .ToString() on the Double and passing that in instead, but I've not been able to find anything on the internet that explicitly says that this is the case. Does anyone know? (If it is equivalent to calling .ToString() then it's a bug in the old application, because it would be appropriate here to stringify the double using the invariant culture)

Macichne Leainig
Jul 26, 2012

by VG
I'm no VB expert either, but I suspect there might be some hidden casting going on there, probably with the "Text As String" bit. If not, there might be another cast somewhere before it goes into the XmlTextWriter.

I would agree that it is probably appropriate to explicitly .ToString() it with the invariant culture.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

Protocol7 posted:

I'm no VB expert either, but I suspect there might be some hidden casting going on there, probably with the "Text As String" bit.

I wasn't clear, but that's the declaration of the method that shows up in intellisense. "Text As String" in VB is the equivalent of "string Text" in C# (unless I have misunderstood)

Macichne Leainig
Jul 26, 2012

by VG

Hammerite posted:

I wasn't clear, but that's the declaration of the method that shows up in intellisense. "Text As String" in VB is the equivalent of "string Text" in C# (unless I have misunderstood)

Oh, duh. That's right. Can you post a snippet of the function call? Could help clear up if there's any unknown behavior.

Adbot
ADBOT LOVES YOU

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Hammerite posted:

I'm creating a new app to replace a horrible old VB.NET app that hasn't been touched for years. This is involving a certain amount of reading the code and also some reverse engineering. I don't know VB from a hole in the ground.

At one point in the code, the VB app calls a method that accepts a String parameter and passes in a Double. Specifically:

Sub XmlTextWriter.WriteString(Text As String)

is being passed the return value of

Function MyClass.MyMethod() As Double

I think this is equivalent to calling .ToString() on the Double and passing that in instead, but I've not been able to find anything on the internet that explicitly says that this is the case. Does anyone know? (If it is equivalent to calling .ToString() then it's a bug in the old application, because it would be appropriate here to stringify the double using the invariant culture)

I've had decent luck with pushing VB .NET through this tool: http://converter.telerik.com/

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply