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

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.

Adbot
ADBOT LOVES YOU

raminasi
Jan 25, 2005

a last drink with no ice

Hammerite posted:

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


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.

Wait, am I overly sleepy? As I read the comments the confusing behavior (at least to me) is that the base class apparently didn't get type-initialized by the call to GetById.

NiceAaron
Oct 19, 2003

Devote your hearts to the cause~

raminasi posted:

Wait, am I overly sleepy? As I read the comments the confusing behavior (at least to me) is that the base class apparently didn't get type-initialized by the call to GetById.

I regret to inform you that you might be overly sleepy. The base class Basic<Ya> did get type-initialized by the call to GetById, because GetById is a member of Basic<Ya>. Since there is no code anywhere in Basic<T> that adds to the instances dictionary, the instances dictionary is empty when GetById executes. The derived class Ya is not type-initialized because you haven't touched that class or any of its members by that point.

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings

Munkeymon posted:

I couldn't figure out what the 'final form' of your example was supposed to be but the inheritance is the point. 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 static stuff is outside inheritence entirely - this is why you can't have static classes that inherit from other classes, for example. And why static methods can't reference base.
Static members belong solely to the class they are declared on, not to parents or children. And while inheritence *does* mean that a Child is a Base and therefore can reference Base's non-private members, it does not mean that Child 'has' StaticMember itself - it means that calling Child.StaticMember is passing straight through to Base.StaticMember - and since that access doesn't involve Child, we wouldn't expect Child to be initialized as the type itself is not being used.

amotea
Mar 23, 2008
Grimey Drawer
R# has a hint that says you probably want to use the base class' name when referring to a base class static member, I guess this is one of the reasons.

NihilCredo
Jun 6, 2011

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

amotea posted:

R# has a hint that says you probably want to use the base class' name when referring to a base class static member, I guess this is one of the reasons.

Meanwhile, in VB.NET, you can happily call a static member from an instance #yolo :v:

Actually, I was curious to see if the same behaviour reproduces in VB.NET, and it doesn't look like it does:

https://dotnetfiddle.net/jMJa6U

code:
mustinherit class Basic(of T)
	protected shared readonly instances as new SortedDictionary(of integer, T)

	public shared function GetById(id as integer) as T 
		return instances(id)
	end function   
end class

class Ya
	inherits Basic(of Ya)
	
	public shared readonly zero as new Ya(0)
    
	private sub new(id as integer) 		
		instances(id) = me
	end sub
end class

try
	console.writeline(ya.getbyid(0))
catch ex as exception
	console.writeline(ex)
end try
		
console.writeline(ya.zero)
console.writeline(ya.getbyid(0))
code:
Ya
Ya
Ya
...while in F# they both throw. Member identifiers are all explicit unlike C#/VB, but I've tried various combinations of Ya.Instances and Basic<'t>.Instances and they all throw:

https://dotnetfiddle.net/zcWUU7

code:
[<AbstractClass>]
type Basic<'t>() =
  static member Instances = SortedDictionary<int, 't>()
  static member GetById id = Basic<'t>.Instances.[id]
  
type Ya(id) as this= 
  inherit Basic<Ya>()
  
  do Ya.Instances.[id] <- this  
  static member Zero = Ya(0)
  
  
try printfn "Ya: %A" (Ya.GetById 0) with exn -> printfn "%A" exn.Message 
try printfn "Ya: %A" (Ya.Zero) with exn -> printfn "%A" exn.Message
try printfn "Ya: %A" (Ya.GetById 0) with exn -> printfn "%A" exn.Message 
code:
"The given key was not present in the dictionary."
Ya: Enz2hgia7049+Ya
"The given key was not present in the dictionary."
I guess the IL could reveal the difference, I might look at it later.

Munkeymon
Aug 14, 2003

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



Cuntpunch posted:

But static stuff is outside inheritence entirely - this is why you can't have static classes that inherit from other classes, for example. And why static methods can't reference base.
Static members belong solely to the class they are declared on, not to parents or children. And while inheritence *does* mean that a Child is a Base and therefore can reference Base's non-private members, it does not mean that Child 'has' StaticMember itself - it means that calling Child.StaticMember is passing straight through to Base.StaticMember - and since that access doesn't involve Child, we wouldn't expect Child to be initialized as the type itself is not being used.

The static members are not separate from the inheritance hierarchy because multiple derived types don't share the static lookup dictionaries.

Dr Monkeysee
Oct 11, 2002

just a fox like a hundred thousand others
Nap Ghost
You guys are using "inheritance" in two ways. Static members are part of the inheritance chain for the purposes of symbol lookup, which is why you can call parent class static members from a child class (or even from an instance of a class). Static members are not overridable and therefore don't participate in the dynamic-dispatch portion of inheritance. Hence referencing a static member from a child class isn't really invoking that child class as far as the CLR is concerned.

Dr Monkeysee
Oct 11, 2002

just a fox like a hundred thousand others
Nap Ghost
I agree this is surprising behavior though. It makes perfect sense when explained but seems extremely easy to overlook when you're just banging out code.

In general you just don't want to mix static data and class inheritance if you can help it.

raminasi
Jan 25, 2005

a last drink with no ice

NiceAaron posted:

I regret to inform you that you might be overly sleepy. The base class Basic<Ya> did get type-initialized by the call to GetById, because GetById is a member of Basic<Ya>. Since there is no code anywhere in Basic<T> that adds to the instances dictionary, the instances dictionary is empty when GetById executes. The derived class Ya is not type-initialized because you haven't touched that class or any of its members by that point.

Oh yeah, I was confusing the KeyNotFoundException with a NullReferenceException.

NihilCredo posted:

...while in F# they both throw. Member identifiers are all explicit unlike C#/VB, but I've tried various combinations of Ya.Instances and Basic<'t>.Instances and they all throw:

Now this I find really surprising.

Mr Shiny Pants
Nov 12, 2012

NihilCredo posted:


I guess the IL could reveal the difference, I might look at it later.
Structural equality?

Nevermind, it would not throw if that was the case.

Munkeymon
Aug 14, 2003

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



NihilCredo posted:

Meanwhile, in VB.NET, you can happily call a static member from an instance #yolo :v:

Actually, I was curious to see if the same behaviour reproduces in VB.NET, and it doesn't look like it does:

https://dotnetfiddle.net/jMJa6U

code:
mustinherit class Basic(of T)
	protected shared readonly instances as new SortedDictionary(of integer, T)

	public shared function GetById(id as integer) as T 
		return instances(id)
	end function   
end class

class Ya
	inherits Basic(of Ya)
	
	public shared readonly zero as new Ya(0)
    
	private sub new(id as integer) 		
		instances(id) = me
	end sub
end class

try
	console.writeline(ya.getbyid(0))
catch ex as exception
	console.writeline(ex)
end try
		
console.writeline(ya.zero)
console.writeline(ya.getbyid(0))
code:
Ya
Ya
Ya
...while in F# they both throw. Member identifiers are all explicit unlike C#/VB, but I've tried various combinations of Ya.Instances and Basic<'t>.Instances and they all throw:

https://dotnetfiddle.net/zcWUU7

code:
[<AbstractClass>]
type Basic<'t>() =
  static member Instances = SortedDictionary<int, 't>()
  static member GetById id = Basic<'t>.Instances.[id]
  
type Ya(id) as this= 
  inherit Basic<Ya>()
  
  do Ya.Instances.[id] <- this  
  static member Zero = Ya(0)
  
  
try printfn "Ya: %A" (Ya.GetById 0) with exn -> printfn "%A" exn.Message 
try printfn "Ya: %A" (Ya.Zero) with exn -> printfn "%A" exn.Message
try printfn "Ya: %A" (Ya.GetById 0) with exn -> printfn "%A" exn.Message 
code:
"The given key was not present in the dictionary."
Ya: Enz2hgia7049+Ya
"The given key was not present in the dictionary."
I guess the IL could reveal the difference, I might look at it later.

F# probably has better ways to express what I'm trying to do, anyway :v:

Scaramouche
Mar 26, 2001

SPACE FACE! SPACE FACE!

.NET Progress Update:
I'm actually doing work and have had my first PR approved!

Cons: Apparently there's this thing called Code Review and man it can get ugly. I think I pissed off a guy by suggesting to use StringComparison.OrdinalIgnoreCase instead of doing x.ToLower = y.ToLower. Now there's a fight that's snarled a bunch of other people on whether we should use an extension method instead of replacing all the current instances of ToLower=ToLower

ljw1004
Jan 18, 2005

rum

Scaramouche posted:

suggesting to use StringComparison.OrdinalIgnoreCase instead of doing x.ToLower = y.ToLower.

I got really confused about this. Aren't there some cases where the two give different answers? Maybe involving the Turkish letters "i"? I remember Neal Gafter being the only one on the Roslyn team who understood cases identity through and through.

B-Nasty
May 25, 2005

Scaramouche posted:

Cons: Apparently there's this thing called Code Review and man it can get ugly. I think I pissed off a guy by suggesting to use StringComparison.OrdinalIgnoreCase instead of doing x.ToLower = y.ToLower. Now there's a fight that's snarled a bunch of other people on whether we should use an extension method instead of replacing all the current instances of ToLower=ToLower

I don't want to work with the kind of guy that would actually try to argue a case for x.ToLower()==y.ToLower() over x.Equals(y, StringComparison.OrdinalIgnoreCase)

I mean, even without looking at the reference source, it's obvious the .Equals method is more performant and easier to read.

Mongolian Queef
May 6, 2004

ljw1004 posted:

I got really confused about this. Aren't there some cases where the two give different answers? Maybe involving the Turkish letters "i"? I remember Neal Gafter being the only one on the Roslyn team who understood cases identity through and through.

Too lazy to check but is ToLowerInvariant the same as StringComparison.OrdinalIgnoreCase maybe?
We always use ToLowerInvariant here.

hackbunny
Jul 22, 2007

I haven't been on SA for years but the person who gave me my previous av as a joke felt guilty for doing so and decided to get me a non-shitty av

Mongolian Queef posted:

Too lazy to check but is ToLowerInvariant the same as StringComparison.OrdinalIgnoreCase maybe?
We always use ToLowerInvariant here.

It's a mess, plus the one person who actually understood it in all of Microsoft died (RIP Michael Kaplan :(). I don't know the answer but, what if I told you that Windows implements case insensitivity (for files, registry keys etc.) by folding strings to uppercase, unlike most other case insensitive comparisons (e.g. C/C++'s stricmp)? and that this actually matters and if you get it wrong, it could be a security issue?

I don't know the answers but boy howdy do I know a lot of thorny questions

Mongolian Queef
May 6, 2004

hackbunny posted:

It's a mess, plus the one person who actually understood it in all of Microsoft died (RIP Michael Kaplan :(). I don't know the answer but, what if I told you that Windows implements case insensitivity (for files, registry keys etc.) by folding strings to uppercase, unlike most other case insensitive comparisons (e.g. C/C++'s stricmp)? and that this actually matters and if you get it wrong, it could be a security issue?

I don't know the answers but boy howdy do I know a lot of thorny questions

Sounds lovely!

rarbatrol
Apr 17, 2011

Hurt//maim//kill.
I deal with unstructured textual data a lot with my job, and I don't know exactly how it works, but yes some Turkish characters have some very unusual characteristics. StringComparison or bust.

Probably much more obvious, but it blew my mind back when I realized that the char type doesn't represent a "true" character, but a Unicode code point.

Nth Doctor
Sep 7, 2010

Darkrai used Dream Eater!
It's super effective!


Scaramouche posted:

I think I pissed off a guy by suggesting to use StringComparison.OrdinalIgnoreCase instead of doing x.ToLower = y.ToLower. Now there's a fight that's snarled a bunch of other people on whether we should use an extension method instead of replacing all the current instances of ToLower=ToLower

:stare: that sounds like hell. Are they doing direct comparisons, too?

ulmont
Sep 15, 2010

IF I EVER MISS VOTING IN AN ELECTION (EVEN AMERICAN IDOL) ,OR HAVE UNPAID PARKING TICKETS, PLEASE TAKE AWAY MY FRANCHISE

ljw1004 posted:

I got really confused about this. Aren't there some cases where the two give different answers? Maybe involving the Turkish letters "i"? I remember Neal Gafter being the only one on the Roslyn team who understood cases identity through and through.

Yup. Turkish has both lowercase and capital forms of "i with dot" and "I without dot", so Turkish "i" capitalizes to "İ" and Turkish "I" lowercases to "ı". This usually completely fucks up using lowercase/uppercase to cheat case-insensitive comparisons. If those letters don't display check out https://en.wikipedia.org/wiki/Dotted_and_dotless_I

...Michael Kaplan of "Sorting it All Out" used to know this inside and out, but unfortunately he died a few years ago.

downout
Jul 6, 2009

OK, how is c# code posted. The bbcode example shows [php] as an example. what's the code for c#?

Moey
Oct 22, 2010

I LIKE TO MOVE IT
Remove the *

[*code=C#]
code here
[*/code]

downout
Jul 6, 2009

Moey posted:

Remove the *

[*code=C#]
code here
[*/code]

Does that do any formatting or is that just whatever formatting comes in from copy/paste?

Moey
Oct 22, 2010

I LIKE TO MOVE IT

downout posted:

Does that do any formatting or is that just whatever formatting comes in from copy/paste?

It says syntax highlighting.

EssOEss
Oct 23, 2006
128-bit approved
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), it would be very questionable practice.

Munkeymon
Aug 14, 2003

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



I didn't think "C#" worked for the code format so I've been using "csharp" :shrug:

Nth Doctor
Sep 7, 2010

Darkrai used Dream Eater!
It's super effective!


Munkeymon posted:

I didn't think "C#" worked for the code format so I've been using "csharp" :shrug:

I wish SQL worked :sigh:

E: for actual content, is it better if: when you have a chain of single liner methods, to make them all async and await the next method in the chain, or leave them without async and not awaiting the next method?

code:

public async Task<int> Foo() { return await Bar(); }

private async Task<int> Bar(){
return await GetIntFromCacheOrDefault();
}
...
Sorry for the formatting, on my phone

Nth Doctor fucked around with this message at 16:07 on Jan 30, 2019

raminasi
Jan 25, 2005

a last drink with no ice

Nth Doctor posted:

I wish SQL worked :sigh:

E: for actual content, is it better if: when you have a chain of single liner methods, to make them all async and await the next method in the chain, or leave them without async and not awaiting the next method?

code:
public async Task<int> Foo() { return await Bar(); }

private async Task<int> Bar(){
return await GetIntFromCacheOrDefault();
}
...
Sorry for the formatting, on my phone

I think it depends on which call stack you'd prefer to see when debugging, which is probably option 1 in most cases.

Nth Doctor
Sep 7, 2010

Darkrai used Dream Eater!
It's super effective!


raminasi posted:

I think it depends on which call stack you'd prefer to see when debugging, which is probably option 1 in most cases.

There's no performance implication for creating state machines all the way through the stack?

E:typo

EssOEss
Oct 23, 2006
128-bit approved
I might worry about it if I called it 50000 times per second. Otherwise, whatever - doesn't matter.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

Nth Doctor posted:

I wish SQL worked :sigh:

E: for actual content, is it better if: when you have a chain of single liner methods, to make them all async and await the next method in the chain, or leave them without async and not awaiting the next method?

code:
public async Task<int> Foo() { return await Bar(); }

private async Task<int> Bar(){
return await GetIntFromCacheOrDefault();
}
...
Sorry for the formatting, on my phone

Await at the point where you need the result. Otherwise just keep bubbling the running task up.

Scaramouche
Mar 26, 2001

SPACE FACE! SPACE FACE!

B-Nasty posted:

I don't want to work with the kind of guy that would actually try to argue a case for x.ToLower()==y.ToLower() over x.Equals(y, StringComparison.OrdinalIgnoreCase)

I mean, even without looking at the reference source, it's obvious the .Equals method is more performant and easier to read.

Yeah it turns out that I might have misunderstood the controversy internally. This kind of comparison isn't endemic throughout the application, only in some extremely old stuff made by people who aren't here any more. I suggested changing out the comparison method, but this old stuff is actually set to be completely refactored by end of Q1 anyway. So the "fight" I referenced isn't about whether to do it or not, it's whether to bother since it's being overhauled in the medium future anyway. No one here seriously believes that ToLower (and in some cases ToUpper I have found since) is the better way of doing things.

As for the different answers, I believe the main difference is as mentioned in the international/non-ASCII case where ToLower/ToUpper might have unintended consequences. I seem to recall someone posting in detail what happens to highbit characters in the poo poo that pisses you off thread way back when. I think they worked in pharmacy and they elucidated all the weird ways that the micrograms character gets messed up by region encoding upper/lower case operations.

LongSack
Jan 17, 2003

OK, this may come across as a bit weird, since I知 not a developer, just a code slinger. I知 giving EF a second chance, now that I have a great book that covers EF Core 2.x.

I知 rewriting my character portfolio app (version 11!). So, there痴 a Character class, and it has a 1 -> many relationship with Attachments, Notes, and several other objects.

I知 using a 2 (3?) level abstraction, where POCO objects are read/written to/from the database using EF, observable DTO objects are presented to the views, and a data abstraction layer exists that consumes POCO objects and produces DTO objects.

My question is this: do I use navigation properties in the POCO objects and let EF handle it with a large number of .Includes, or do I put that on the DTO objects?

Like a character has attachments. I can have an ICollection<Attachment> on the Character object, or I can have an ObservableCollection<Attachment> on the CharacterDTO object and leave the filling of it to the DAL.

Thoughts?

SimonChris
Apr 24, 2008

The Baron's daughter is missing, and you are the man to find her. No problem. With your inexhaustible arsenal of hard-boiled similes, there is nothing you can't handle.
Grimey Drawer

LongSack posted:

OK, this may come across as a bit weird, since I知 not a developer, just a code slinger. I知 giving EF a second chance, now that I have a great book that covers EF Core 2.x.

I知 rewriting my character portfolio app (version 11!). So, there痴 a Character class, and it has a 1 -> many relationship with Attachments, Notes, and several other objects.

I知 using a 2 (3?) level abstraction, where POCO objects are read/written to/from the database using EF, observable DTO objects are presented to the views, and a data abstraction layer exists that consumes POCO objects and produces DTO objects.

My question is this: do I use navigation properties in the POCO objects and let EF handle it with a large number of .Includes, or do I put that on the DTO objects?

Like a character has attachments. I can have an ICollection<Attachment> on the Character object, or I can have an ObservableCollection<Attachment> on the CharacterDTO object and leave the filling of it to the DAL.

Thoughts?

I am using pretty much the same architecture in my app, and it never even occurred to me not to use Include to fetch navigation properties. Won't doing it the latter way require several round-trips to the database to fetch all the related data types? Is there some advantage to this approach that I am missing?

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)?

LongSack
Jan 17, 2003

SimonChris posted:

I am using pretty much the same architecture in my app, and it never even occurred to me not to use Include to fetch navigation properties. Won't doing it the latter way require several round-trips to the database to fetch all the related data types? Is there some advantage to this approach that I am missing?

Well, the main window is a toolbar, some other universal stuff, and a listbox that lists the character痴 portrait next to their name, nickname, and some other demographic data all from the Character object, which does have navigation properties for Race, Handedness, Gender, and Portrait.

The idea is not to load up all the 1 -> many properties until a character is selected, which brings up a different interface for that character with attachments, notes, related characters, full demographic data, etc.

EssOEss
Oct 23, 2006
128-bit approved

Hammerite posted:

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

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

Mr Shiny Pants
Nov 12, 2012

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 is nice, I forgot about the umlaut.

Adbot
ADBOT LOVES YOU

SimonChris
Apr 24, 2008

The Baron's daughter is missing, and you are the man to find her. No problem. With your inexhaustible arsenal of hard-boiled similes, there is nothing you can't handle.
Grimey Drawer

LongSack posted:

Well, the main window is a toolbar, some other universal stuff, and a listbox that lists the character痴 portrait next to their name, nickname, and some other demographic data all from the Character object, which does have navigation properties for Race, Handedness, Gender, and Portrait.

The idea is not to load up all the 1 -> many properties until a character is selected, which brings up a different interface for that character with attachments, notes, related characters, full demographic data, etc.

In that case, I would make one API endpoint that returns a list of CharacterListItemDTO's for the listbox, and another endpoint that return a FullCharacterDTO, with all character data, including navigation properties. The latter endpoint would .Include() the navigation properties, whereas the former would just ignore them.

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