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
Hammerite
Mar 9, 2007

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

epalm posted:

How do y'all solve the problem of setting the Owner property on modal dialogs in WPF, while using IoC and MVVM patterns? I've seen (and written) all sorts of hacks.

Here's a naive example of a window with a viewmodel containing an ICommand which prompts the user. It works by setting vm.Owner = this, and then using Owner later in a MessageBox when the command is called.

C# code:
...
This works, but the ViewModel is doing View-related things like opening message boxes, and knowing who owns it.

Here's an example that abstracts the dialog into a Func, which is set in the constructor of the Window.

C# code:
...
This feels better, now my ViewModel calls OkCancelBox and gets back a MessageBoxResult, without knowing anything about the View. If you "forget" to set OkCancelBox, you'll get a NotImplementedException. In a test, I can trivially set the Func to simulate the user clicking "OK":

C# code:
vm.OkCancelBox = s => MessageBoxResult.OK;
Still, I'm not sure I like it.

There are other solutions like passing in (or service-locating) an IMessageBoxService, but I don't fully understand how you can specify the owner of a dialog without the ViewModel knowing who owns it.

What do you do?

I am pretty sure I've used code similar to your callback example to do this kind of thing. I went looking for an example in the code for an app I was working on a few months ago (now being maintained by another team), but my recollection of where to find it didn't hold up.

Adbot
ADBOT LOVES YOU

Hammerite
Mar 9, 2007

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

Munkeymon posted:

Today I learned that when the manual says that "A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced." that does not apply to the static constructors of types derived from the class containing the aforementioned static member, even if it's a template/generic class.

So let me see if I understand - you're saying

code:
class Program
{
    class C1
    {
        public static int x = 0;

        static C1()
        {
            Console.WriteLine("static constructor of C1");
        }
    }

    class C2 // edit: what a fuckwit, see my later post
    {
        static C2()
        {
            Console.WriteLine("static constructor of C2");
        }
    }

    static void Main(string[] args)
    {
        int x = C1.x;
    }
}
doesn't print out the "static constructor of C2" message? I tried it and indeed only the "C1" message prints, but I don't understand why you'd expect behaviour different from that.

Hammerite fucked around with this message at 11:03 on Jan 22, 2019

Hammerite
Mar 9, 2007

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

Hammerite posted:

So let me see if I understand - you're saying

code:
class Program
{
    class C1
    {
        public static int x = 0;

        static C1()
        {
            Console.WriteLine("static constructor of C1");
        }
    }

    class C2
    {
        static C2()
        {
            Console.WriteLine("static constructor of C2");
        }
    }

    static void Main(string[] args)
    {
        int x = C1.x;
    }
}
doesn't print out the "static constructor of C2" message? I tried it and indeed only the "C1" message prints, but I don't understand why you'd expect behaviour different from that.

Look at this huge idiot who didn't make C2 inherit from C1.

I fixed it and the same behaviour is observed - and the same behaviour is observed even if Main() refers to x as "C2.x", which I can understand being a little surprising. But presumably the C# compiler transforms it into a reference to C1.x, since C1 is where it's actually defined and it's all known about at compile time, so not that surprising?

Hammerite
Mar 9, 2007

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

Munkeymon posted:

No, it's like this:
C# code:
abstract class Basic<T> {
    protected static readonly SortedDictionary<int, T> instances = new SortedDictionary<int, T>();

    public static T GetById(int id) {
        return instances[id];
    }
}

class Ya : Basic<Ya> {
    public static readonly zero = new Ya(0);
    
    private Ya(int id) {
        instances[id] = this;
    }
}

void Main() {
    try {
        Ya.GetById(0);//error because the dict is empty
    }
    Ya.zero; //works fine
    Ya.GetById(0); //works now because the previous line triggered initializer
    //IIRC that member access makes all the member constructors run but
    // I can't be assed to recreate the tests I did on that.
}
This is Surprising Behavior because I wouldn't expect the members of Basic<T> to exist absent the derived type since it's a generic class and, conceptually, doesn't really exist on its own terms.

This is basically the example I gave, but dressed up with some unnecessary and distracting extra elements (the base class is generic and abstract). GetById() is defined on the base class, so when you access it (irrespective of the fact that you're referring to it as Ya.GetById()), the base class static constructor runs. You're not accessing anything that's actually part of Ya in that line.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Also, you say that Basic<T> "conceptually doesn't exist on its own terms". I wouldn't presume to tell you how you ought to conceptualise things, but to say that Basic<T> doesn't exist on its own terms isn't true in .NET. It would be true of a template class in C++, say.

Hammerite
Mar 9, 2007

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

Munkeymon posted:

I couldn't figure out what the 'final form' of your example was supposed to be but the inheritance is the point.

In turn, I'm not sure what you mean by "final form", which leads me to wonder whether we are talking past each other.

Munkeymon posted:

It's surprising that only part of the inheritance hierarchy was initialized, but becomes less so when you find out that, since 4.0 (the framework, not the compiler), statics are so aggressively lazily initialized that it will avoid initializing the value of a static property as long as it can.

But I'm saying it's not surprising at all. You were accessing a static member of the base class, so the base class got type-initialised. The member you were asking for didn't have anything to do with the derived class, so the derived class didn't get type-initialised.

Hammerite
Mar 9, 2007

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

EssOEss posted:

I think it is a general good programming practice to use APIs for what they are intended for. Does ToLowerInvariant() say "compare" anywhere? No. So don't use it for comparisons. Even if it happened to do the right thing (which it doesn't),

Can you elaborate on this? You have in mind strings s1 and s2 for which (s1.ToLowerInvariant() == s2.ToLowerInvariant()) != s1.Equals(s2, StringComparison.InvariantCultureIgnoreCase)?

Hammerite
Mar 9, 2007

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

EssOEss posted:

Sure. But I want to emphasize that I think that is only a small aside to the real issue - using letter-case APIs for comparison is bad software design already on general principles, even if they worked the same.

C# code:
var a = "Waldstraße";
var b = "Waldstrasse";
Console.WriteLine(a.Equals(b, StringComparison.InvariantCultureIgnoreCase));
Console.WriteLine(a.ToLowerInvariant() == b.ToLowerInvariant());
Try it out

That's a useful demonstration, thanks.

Mr Shiny Pants posted:

That is nice, I forgot about the umlaut.

You mean the "ß"? That's a "sharp S" or Eszett. "Umlaut" is an alternative name for the diaeresis accent (two dots over a letter).

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)

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)

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Thanks for all the suggestions. Yes, the project file specifies <OptionStrict>Off</OptionStrict>. I followed the advice to look in ILSpy. It looks like what is actually happening is this:

xmlTextWriter.WriteString(Conversions.ToString(Me.someDateTime.TotalDays))

so the method actually being used to convert is Microsoft.VisualBasic.CompilerServices.Conversions.ToString(double). Unfortunately that documentation doesn't say what conventions are used to convert or whether it uses the user's current culture, but I still suspect that is what it does.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
It looks like it does use CurrentThread.CurrentCulture (or CurrentUICulture, whichever)

code:
Sub Main()
    Dim kEN As String = 2.1
    Console.WriteLine(kEN) REM prints 2.1
    System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.CreateSpecificCulture("fr-FR")
    System.Threading.Thread.CurrentThread.CurrentUICulture = System.Globalization.CultureInfo.CreateSpecificCulture("fr-FR")
    Dim kFR As String = 2.1
    Console.WriteLine(kFR) REM prints 2,1
End Sub

Hammerite
Mar 9, 2007

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

That's horrible, you should post it in the coding horrors thread

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
TFW when you mean to write C# but end up writing PHP instead

Hammerite
Mar 9, 2007

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

roadhead posted:

I need to know what PDF library will let me easily generate my own PDFs in .NET (mostly just compositing a few images and adding some text regions, though later I'll want to combine multiple existing single page PDFs into one big one...)


UI Project is Framework 4.7 (WPF) and my Model is Core 2.0 - so obviously native C# solutions preferred :)

IronPDF had the best looking web site, but this iTextSharp NuGet package has the most downloads, though PDFsharp isn't far behind it...

ugh I wish It was easier to see licensing information from the NuGet manager.

Does it have to be free, or are paid-for products acceptable?

We use DynamicPDF for some stuff at work and it's ok for what we do, but it costs money. No idea whether they've released it as .NET Standard/.NET Core, and I haven't tried anything else for comparison.

Hammerite
Mar 9, 2007

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

roadhead posted:

Free isn't a requirement, but it does need to SHIP in my software that people install on their machines, so I'd have better luck getting approval for something that has a one time cost instead of a licensing model where we have to pay per instance.

???

It is a one-time cost, as far as I understand it. You buy one of the "developer" licenses and you can develop applications with it and distribute them without requiring the end user to purchase licenses. If it doesn't work like that then my company is doing it wrong (!)

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
We use Wix for some end-user software at my place and when I've had to interact with it I've found it to be a bit complicated, but very flexible and ultimately I could do everything I needed to with it. I put the complexity of it down to installation on Windows just being complicated in general.

Whatever you do, don't try to roll your own installer.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Added a unit test project to a solution I'm working on as a personal project. The unit test project itself is .NET Core 2.1 and the project it is testing is .NET Standard 2.0.

Foo.Bar.csproj:
code:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <AssemblyName>Foo.Bar</AssemblyName>
    <RootNamespace>Foo.Bar</RootNamespace>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
    <WarningsAsErrors />
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  </PropertyGroup>

</Project>
Foo.BarTests.csproj:
code:
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.1</TargetFramework>

    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
    <PackageReference Include="MSTest.TestAdapter" Version="1.3.2" />
    <PackageReference Include="MSTest.TestFramework" Version="1.3.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Bar\Foo.Bar.csproj" />
  </ItemGroup>

</Project>
When I run the tests in the project by clicking "Run All" in the Test Explorer pane, VS prints out this message:

[22/06/2019 21:42:42 Warning] Test run will use DLL(s) built for framework .NETCoreApp,Version=v1.0 and platform X86. Following DLL(s) do not match framework/platform settings.
Foo.BarTests.dll is built for Framework 2.1 and Platform AnyCPU.
Go to http://go.microsoft.com/fwlink/?LinkID=236877&clcid=0x409 for more details on managing these settings.

Why the hell is it printing this warning? What's "Framework 2.1"? That link printed as part of the message is to a page with a "this page is no longer being updated" banner at the top, that talks about the difference between 32-bit and 64-bit tests - it seems totally irrelevant.

The tests do actually run, but I'm paranoid that it'll arbitrarily stop running them at some point in the future. The reason I am paranoid about this is that at work, we had a build that was supposed to be running unit tests as part of the build process, but after an upgrade of the toolset on the build server the unit tests stopped running because they were no longer being detected (and Microsoft had made the extremely stupid decision that if you pointed vstest.console.exe at a DLL and told it "hey go run the tests in this DLL", and it didn't find any tests in the DLL, it should just print out a warning instead of what would be sensible, i.e. returning a failure code which would have caused our build to fail). So for a period of months we weren't running the unit tests we thought we were running on every build.

I'm running VS version 16.1.3. If I go Help -> Check for Updates, Visual Studio claims that it is up-to-date.

Hammerite
Mar 9, 2007

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

Bruegels Fuckbooks posted:

Have you tried making a runsettings file?

I would suggest providing a run settings file and having it specify TargetPlatform, etc. explicitly (rather than relying on it figuring out what to do by itself.)

I had not tried that. I tried adding this file and telling VS to use it via Test -> Test Settings -> Select Test Settings File:

code:
<?xml version="1.0" encoding="utf-8"?>

<RunSettings>
  <RunConfiguration>
    <MaxCpuCount>1</MaxCpuCount>
    <ResultsDirectory>.\TestResults</ResultsDirectory>
    <TargetPlatform>x64</TargetPlatform>
    <TargetFrameworkVersion>FrameworkCore10</TargetFrameworkVersion>
  </RunConfiguration>
</RunSettings>
The error message still appears (but with "x86" replaced with "x64"). I think the real thing I need to be on top of is framework versions rather than whether the CPU is x86 or x64 (I could be wrong about that)...

I did try "FrameworkCore21" and "FrameworkCore20" as the TargetFrameworkVersion - neither of those is valid, it prints out an error message and doesn't run the tests.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Yes, it looks like that. Thanks for finding that. OK, I guess I'll just have to keep an eye on it.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Any WPF/XAML experts? I have a toggle button (more exactly a Telerik RadRibbonToggleButton) and when it is checked it turns a really strong shade of blue, the same colour as the window's title bar. I don't like this. I want to be able to control what shade of blue it turns when checked. I have completely failed to find a way to do this. I tried setting the Background property of the button; it works as long as the button isn't checked, but gets ignored when the button is checked - it turns the same shade of blue. I tried modifying the Style in the XAML file so that it sets the Background property and has a trigger to set the Background differently when the button "IsChecked". Predictably, this also did not work. I saw a StackOverflow post that suggested changing the Template that applies to toggle buttons. I tried doing that and it just hosed up all the toggle buttons so that the icons don't display, which is obviously no good.

I have non-toggle Buttons on this Ribbon whose background colour I can control. I could therefore achieve what I want to do by changing the toggle buttons into normal Buttons and setting the background on them by binding to properties, but this seems like hacking around my own lack of understanding.

Hammerite
Mar 9, 2007

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

Careful Drums posted:

Wild guess: have you changed your machine's desktop color theme, and does changing it affect how the toggle button looks when it's enabled? I've used some apps where buttons and other ui elements turn pink to match my ridiculous color theme.

Not that "change your theme" is a practical workaround, but it might explain why it's not responding to the things you've tried changing so far.

I haven't changed the machine's desktop color scheme to my knowledge.

I just tried opening up the "themes and related settings" widget and selecting a different colour. It caused the taskbar and various window title bars to change color, but nothing changed about the app I'm working on, even after I closed and reopened it.

Hammerite
Mar 9, 2007

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


NoDamage posted:

Telerik has its own set of themes which have custom styles defined for all of their widgets (and also style overrides for all of the system widgets). What theme do you have set up for Telerik? Essentially what you'll need to do is create a custom style that inherits from the default RadRibbonToggleButtonStyle and then override the appropriate properties for the checked state, and/or possibly override the control template as well, but the specifics will depend on exactly which theme you're using.

Thanks for the tips. I can see I need to get a better understanding of how Telerik's styles work if I want to take this any further.

Hammerite
Mar 9, 2007

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

nielsm posted:

I have two WPF (.NET Framework 4.5) applications. I'd like to make a sort of soft integration between them: If both are installed, one can detect the other is installed, and offer to send a message to the other. If the other is already running, the message is sent to the existing instance, otherwise start a new instance.
This sounds very similar to what an out-of-process COM server can do, but it seems that pattern is not well supported by .NET Framework.
Any suggestions for other technologies to look at, or should I just do something "stupid" with Win32 messages, named pipes, or similar?

This might be useful:

http://blogs.microsoft.co.il/arik/2010/05/28/wpf-single-instance-application/

Hammerite
Mar 9, 2007

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

Cuntpunch posted:

To the original question: this is kind of a tangent answer, that mostly worries about using mutex's to ensure that an app only runs once. (It also uses Remoting, so it won't be portable to .NET Core)
In terms of cross-process communication like that, you've got a couple of problems to solve:
-Identifying that the other product is installed
-Talking to it

In terms of identification - are these both in-house applications with well-known identities? In terms of going 'hey, is this installed?' that sounds a lot like a problem for just peeking at the registry(either for your own key, or Windows's own install list) and/or filesystem.

In terms of talking to it, how robust of a communication protocol do you need? Does the other process need to send responses? Named pipes have pretty clean support in .NET and are simple enough to get going. Do you NEED a super-fancy solution like a middleman service?


But back to that link:
I've only skimmed it a bit, but what's all the ceremony about? Is there some ultra-harmful thing in the direct solution of 'check for existing Mutex, exit out immediately if it exists, otherwise create it'?

I can't comment on how it compares to your suggestion of using a Mutex because I haven't tried my hand at implementing that. Looking at relevant questions on StackOverflow, it appears that there are a lot of details to get right if you do go down that route: https://stackoverflow.com/questions/229565/what-is-a-good-pattern-for-using-a-global-mutex-in-c/229567

The reason I posted it is that nielsm specifically mentioned the need to handle messages in an existing instance of the application or start a new instance, depending on whether an instance exists - and I knew that this code supports that use case, because we use this approach in an application I was involved in developing last year that does exactly that.

Hammerite
Mar 9, 2007

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

Ape Fist posted:

...

This worked, but I have 3 questions.

1. Why did it work?

2. Is this bad practice?

3. What is better practice?

The only thing you did with the argument is call its method GetType(). GetType() is a method on object. Every non-static type inherits from object (either directly or indirectly), so that's OK to do regardless of the type of object. If you were doing something with your Any that required more of it than just being an object, you would have had to place constraints on it or the compiler wouldn't have allowed it.

But as someone else said, you could have instead declared the parameter to be of type object and that would arguably be better style since what you're using is the fact that the parameter is of type object; you're not really using the "genericness" of the method.

Hammerite
Mar 9, 2007

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

LongSack posted:

How do you all handle string literals? For strings that appear more than say 2 or 3 times, I have a Constants class (which also has ‘numbers’ like exit codes, etc.), but for strings that are one-offs, I just code them inline. Obviously my apps are not internationalized. I recently installed a Microsoft code analyzer (and quickly uninstalled it) which bitched at literally every string literal saying it should be from a resource dictionary instead.

So what do you all do?

I have sometimes done this Constants class thing you describe, but it's usually for things that are internal and not user-facing, e.g. the names of config setting keys, default values for configuration settings, and such. I'm not sure I'd store messages for the user that way, even if the app isn't internationalised and the strings are all in English I might keep them closer to the point of use - or in any case, separate from the more "techy" constants.

e: when I've done this it has been in ASP.NET projects. It's convenient to be able to write using static MyProduct.MyProject.Constants; at the top of Startup.cs.

Hammerite fucked around with this message at 09:53 on Aug 20, 2019

Hammerite
Mar 9, 2007

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

Jabor posted:

I mean, why not just use resources like the analyser suggests? It's easy enough to set up, and once you've done it the usage is basically identical to your constants class anyway.

I would like to preface this by saying that I don't want to appear emotionally invested in doing things the way I described in my post, so please don't interpret this that way.

Your suggestion is devoid of reasons to do as you suggest. You say that it's "easy enough to set up" - OK, but that's not a reason to do as you say, it's merely the absence of one specific reason not to. It isn't, on its own, a basis for choosing a course of action. And the alternative you're contrasting it with (creating a constants class) is at least equally easy; easier if you haven't set up a resource dictionary before, because it doesn't come with the overhead of having to figure out how to do that (whereas I already know how to type stuff into a file of C# source).

In addition, there is a heuristic that applies to suggestions that run along the lines of "why not do things using (piece of machinery that's part of the framework)?" which is "does it seem like the kind of thing that will at some point become 'the old way' of doing things?" Maybe resource dictionaries have been around since .NET 1.0 and are unlikely to ever change, or maybe they were added last week and will change at the drop of a hat, I don't know. But there are aspects of .NET these days that change rapidly (particularly in the area of ASP.NET Core), and if something smells like a thing that will become obsolete at some point, that's a basis for being wary of it. To me, as someone who doesn't know what a resource dictionary is or how stable a feature it is, your suggestion has that smell.

e: I would follow this up by acknowledging that the considerations I outline in the previous paragraph are very weak reasons for choosing a course of action, and might be easily overturned by somebody posting a list of reasons why resource dictionaries are actually really good and worth using. However, weak as they are, they are still more than the zero reasons you provided.

Hammerite fucked around with this message at 11:18 on Aug 20, 2019

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
I am familiar with SQL but I don't use the SQL-like syntax for LINQ in C# code. I find it jarring for a couple of reasons, I think: firstly it is different enough from SQL that the similarities are not really helpful, secondly I don't find it easy to think in the two different paradigms when they are brought into such close juxtaposition. If I'm looking at the C-style syntax of C# code, I prefer to think in the "do this with it, then do this, then do this" paradigm of the method-chaining syntax.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
When you throw an ArgumentException, should the paramName always be the name of the parameter to the immediate method? Or can/ought it be the name of the parameter to the publicly-visible method that was called by client code?

Like if I have a public method Foo(string s) and a private method Bar(int x) and Foo calls Bar which throws ArgumentException because there is something wrong with x (which was derived from s, and let's assume here that I know when throwing that there must have been something wrong with s and what that something is). Ought the paramName to be "x" (because that's what the parameter to Bar was called), or ought it to be "s" (because that's the name of the faulty parameter to the publicly-visible method that got called)? Or is it a matter of style?

Hammerite
Mar 9, 2007

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

SirViver posted:

The outside caller doesn't and shouldn't care about the classes private implementation details, but conversely and IMO more importantly the private method also shouldn't "know" where it is called from. If you ever call that private method from two different points then referring to one specific caller's parameter name would end up being completely wrong.

You are correct about this. I omitted from my original post that I hadn't meant to imply that there should be any coupling between the public method and the private one. The implementation I had in mind (which wasn't communicated in my post) is that if Bar() were to use Foo()'s name for the parameter, it would have to take an additional string parameter which would be the name of the parameter to the public method.

code:
public void Foo(string s)
{
    Bar(int.Parse(s), nameof(s));
}

private void Bar(int x, string parameterName)
{
    if (x <= 0)
    {
        throw new ArgumentException("Should be positive", parameterName);
    }
}

Hammerite
Mar 9, 2007

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

Bruegels Fuckbooks posted:

I would only ever throw an argument exception on a public method - the point of the argument exception is to tell the caller that they're using the class incorrectly.

The reasoning you purport to give here is a non-sequitur. There is no reason why the public method can't delegate its checks on its arguments to another method (private or otherwise), and in that way ensure that the caller is told that they're using the class incorrectly.

Perhaps I have a lot of checks to run against the argument, and the entire purpose of that private method is "check this argument for things that could be wrong with it" - and I want the call to it to be a one-liner, the better to keep the implementation of the public method simple and readable.

Both of you advocate for putting checks in the public method itself - not indirectly via another method - either in the form of checking parameters or wrapping exceptions coming from the private method. The problem I have with this is that it's boilerplate. What if I have several public methods that all take the same parameter, and I want to carry out the same check (or laundry list of checks) on that parameter in each one? The proposition that one should repeat the same checks on the argument in each of the public methods directly contradicts the idea of "don't repeat yourself"! The suggestion of wrapping the private method call in a try-catch and rethrowing is less bad but is still boilerplate.

Hammerite
Mar 9, 2007

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

SirViver posted:

Well, if the sole purpose of "Bar" is to only validate a parameter (so it would in actuality be something like "ValidatePositiveInteger" or whatever), then that seems perfectly fine to me as the parameter name is actually relevant information to that method.
E: ^ You really should have specified so in your original question however, as that detail actually makes a whole lot of difference :)

Yes, it is the problem everyone runs into occasionally where you have been thinking about something for a while and when you come to explain it to someone else you omit important details because you forget that they need to be explained because they seem obvious to you (because you have been taking them for granted in your thinking for a while).

The limited endorsement of using the public method's name for the parameter reassures me somewhat that my thinking isn't too out of whack on this. The reason why I'm humming and hahing about it is that code I'm writing at the moment will typically be used elsewhere in the same solution, so in a context where a full stack trace from inside the library is available. I'm not sure whether it would be unhelpful to use the name of the parameter at the class's top-level interface, when from the point of view of someone reading the stack trace that's somewhere in the middle of the call stack - therefore it might seem more arbitrary than just using the parameter name for the method where the exception was actually thrown.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Forgive me if it's a stupid question, I didn't take the time to understand all of that, but couldn't you just pass around object instead of object[] ? An array is an object. You can just require that the invokee accept object and it has to know that it'll be of type object[] (or whatever else).

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Am I going mad or have they removed "trim trailing whitespace" from the latest version of visual studio? What a bizarre thing to get rid of

Hammerite
Mar 9, 2007

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

SirViver posted:

You can use IReadOnlyList<Monster> and return (an ideally cached instance of) monsters.AsReadOnly() to prevent your list from being modified.

I don't really understand the purpose of the AsReadOnly() method (in general, not just here). Surely you expose a property of type IReadOnlyList<Monster> and just return your list of monsters.

Hammerite
Mar 9, 2007

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

SirViver posted:

No, because then you can just cast back to List and modify all you want.

I know you can. So what?

quote:

AsReadOnly wraps the list in an actual ReadOnlyCollection that doesn't expose the list instance and throws on any attempt to add or remove items. That way you also protect against accidental modification, e.g. by code that just takes an object (your cast-only IReadOnlyList) and does itself a typecheck for "if (obj is ICollection<T> col && !col.IsReadOnly)" or similar.

Anybody who does that deserves what they get

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Just decided to resume work on a personal project after a month away from it, Visual Studio asks to update itself and I say yes... and now I have a bunch of uh, whatever this is:

code:
2>Path\Site.csproj : warning NU1701: Package 'System.ComponentModel.Annotations 4.7.0' was restored using '.NETFramework,Version=v4.6.1, .NETFramework,Version=v4.6.2, .NETFramework,Version=v4.7, .NETFramework,Version=v4.7.1, .NETFramework,Version=v4.7.2, .NETFramework,Version=v4.8' instead of the project target framework '.NETStandard,Version=v2.1'. This package may not be fully compatible with your project.
2>Path\Site.csproj : error NU1202: Package Microsoft.AspNetCore.Components.WebAssembly 3.2.0 is not compatible with netstandard2.1 (.NETStandard,Version=v2.1). Package Microsoft.AspNetCore.Components.WebAssembly 3.2.0 does not support any target frameworks.
2>Path\Site.csproj : error NU1202: Package System.Net.Http.Json 3.2.0 is not compatible with netstandard2.1 (.NETStandard,Version=v2.1). Package System.Net.Http.Json 3.2.0 does not support any target frameworks.
(like two dozen more in the same vein as those last 2)
This project was building before so I have no idea why these packages are apparently incompatible suddenly. the Team Explorer panel indicates that there are no changes, so the files are the same as the ones that previously worked.

Microsoft's docs on NU1202 say "Solution: Change the project's target framework to one that the package supports." but um... the error messages say the packages don't support any target framework. so how would I do that?!

e: and I did a clean and rebuild, deleted the bin and obj folders for all projects in the solution, restarted even though the VS installer didn't ask me to, this still happens.

e: also tried
code:
dotnet nuget locals all --clear
didn't help either

Hammerite fucked around with this message at 21:02 on Sep 6, 2020

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe
Well, it turns out there were minor-minor updates to the 4 nuget packages I actually had references to (they went from 3.2.0 to 3.2.1) and installing those solved the problem. VS's nuget interface refused to update them though, I had to uninstall the packages and then reinstall them. super unhelpful behaviour from VS - if it had just said "you must update these packages because we hosed around with something" obviously I would have done.

Adbot
ADBOT LOVES YOU

Hammerite
Mar 9, 2007

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

biznatchio posted:

Calling .Result doesn't affect which thread the task runs on. It just blocks the current thread and puts it to sleep until the task is done. The task itself will keep running wherever it needs to, which can really gently caress you in a single-threaded synchronization context like WPF or WinForms because it's a recipe to deadlock -- the task you're waiting on might require the same thread you just blocked to wait for it.

That's why library authors are encouraged to use ConfigureAwait; because it frees them from accidentally contributing to a deadlock in the event that a consumer of the library uses .Result.

(Don't ever use .Result. Not just because you break the whole idea of asynchronous processing if you're blocking threads, but because you should be calling .GetAwaiter().GetResult() instead; which does exactly the same thing as .Result except it doesn't wrap any exceptions the task throws inside an AggregateException, you just get the raw exception instead.)

This is good to learn about. On a previous project I was faced with a need (well, not a need, but it seemed like the right way to go) to make some synchronous functionality async. But there were existing consumers of the synchronous API that I didn't want to interfere with, but I also didn't want to have two versions of the code. So I ended up writing a TaskExtensions class so that if you had a Task t you could do t.Wait_RethrowingOriginalException() or if you had a Task<T> t you could do result = t.Result_RethrowingOriginalException(). The *_RethrowingOriginalException() methods weren't very big but they had to mess around with ExceptionDispatchInfo in order to throw the inner exception of an AggregateException with its original stack trace. If I'm understanding correctly, a better way would have been GetAwaiter().GetResult()?

Although I notice that in the docs for Task.GetAwaiter it claims that "This method is intended for compiler use rather than for use in application code"

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