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
downout
Jul 6, 2009

New Yorp New Yorp posted:

code:
var lazyPersonFactory = new Dictionary<string, Func<IThingy>> 
{
    { "Bar", ()=>new Bar() },
    { "Baz", ()=>new Baz() }
};
etc

[edit]
this still smells like a design problem, specifically a dependency inversion problem. Nothing should be "asking" for a specific type. The specific type should be determined and provided by the askee.

You could be right, but I can't envision any other way it would be done. I'm trying to setup a library that does a set of operations on a various set of IModel, but the constructor classes for IModel need to be supplied in some way by an external process. The only way I can imagine this happening is though some registration process.

Adbot
ADBOT LOVES YOU

NihilCredo
Jun 6, 2011

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

downout posted:

You could be right, but I can't envision any other way it would be done. I'm trying to setup a library that does a set of operations on a various set of IModel, but the constructor classes for IModel need to be supplied in some way by an external process. The only way I can imagine this happening is though some registration process.

I'm not sure what you mean by "constructor classes". What does IModelConstructor look like? What do you mean by "an external process"? Marshalled classes through named / unix pipes?

brap
Aug 23, 2004

Grimey Drawer

Shy posted:

not a particularly complex idea

Seriously do whatever you want for your personal learning project but don’t believe this in the context of software.

downout
Jul 6, 2009

NihilCredo posted:

I'm not sure what you mean by "constructor classes". What does IModelConstructor look like? What do you mean by "an external process"? Marshalled classes through named / unix pipes?

I'm just calling it a constructor class, that might be a bad name. It's a class with a method that takes an object as a parameter and constructs a poco object and returns it.

The method would look like:
C# code:
POCOA GetModel(ConcreteType item)
{
  var returnObj = new POCOA();
  returnObj.FieldA = item.GetValue("fieldA");
  ...
  return returnObj;
}
Many concrete implementations could construct and return their own poco classes. If a library wanted to use the various GetModel methods then it would need to be supplied with a class that could provide the classes that implemented the GetModel methods.

downout fucked around with this message at 23:17 on Feb 9, 2019

raminasi
Jan 25, 2005

a last drink with no ice

downout posted:

shenanigans

There's a serious X/Y smell here. What are you ultimately trying to accomplish?

FrantzX
Jan 28, 2007
Are you trying to re-create Automapper?

downout
Jul 6, 2009

FrantzX posted:

Are you trying to re-create Automapper?

It's similar, and I should probably look into what automapper can do when mapping more complex classes.

raminasi posted:

There's a serious X/Y smell here. What are you ultimately trying to accomplish?

There's lists of various poco classes that I want to put into a cache. The whole cache portion I was trying to build out into a library. The main application that uses the library would need to implement the various poco constructor classes, and then provide them to caching library so it could retrieve the data and update caches when data changes.

sadus
Apr 5, 2004

Cache them as JSON in Redis? Are they not serializable?

raminasi
Jan 25, 2005

a last drink with no ice

downout posted:

It's similar, and I should probably look into what automapper can do when mapping more complex classes.

Well, beware; if you're using AutoMapper to map complex classes, you probably shouldn't be using Automapper.

downout posted:

There's lists of various poco classes that I want to put into a cache. The whole cache portion I was trying to build out into a library. The main application that uses the library would need to implement the various poco constructor classes, and then provide them to caching library so it could retrieve the data and update caches when data changes.

So you're trying to create one cache type that can cache multiple types of object that aren't known at compile time? And the cache is responsible for creating and updating each of these types, so it needs to know how to do it?

If I understand correctly (and I very well might not) that seems like a lot of added complexity for I'm not sure what benefit. Why don't you know the set of cacheable types beforehand? Why does the cache need to also do its own object construction/modification?

brap
Aug 23, 2004

Grimey Drawer
Once you stray from the use case of "I need to convert from a type I don't control to a type I do control" you should be reconsidering AutoMapper IMO.

If you're registering a lambda for every conversion you need to do in your app that's a bad smell.

downout
Jul 6, 2009

raminasi posted:

Well, beware; if you're using AutoMapper to map complex classes, you probably shouldn't be using Automapper.


So you're trying to create one cache type that can cache multiple types of object that aren't known at compile time? And the cache is responsible for creating and updating each of these types, so it needs to know how to do it?

If I understand correctly (and I very well might not) that seems like a lot of added complexity for I'm not sure what benefit. Why don't you know the set of cacheable types beforehand? Why does the cache need to also do its own object construction/modification?

That's actually pretty close. The event that triggers the cache can only provide a unique id to get the item that needs updated and a string that identifies the type. I had hoped to make it generic enough that it could use that to get the object that needed updated and add to the cache. It didn't really need to know the type beyond the type implementing a basic interface.

To get the object to be updated, I thought it would basically be able to take that string and get a model constructor that implemented an interface with the method it needed.

EDIT: Also, the main application would know all of the cacheable types beforehand and implement all the classes to work with them as needed. I got all of this working in the main application, but I had hoped to break it out into it's own stand-alone library because it doesn't seem to need to "know" much about anything it's caching.

downout fucked around with this message at 04:22 on Feb 10, 2019

NihilCredo
Jun 6, 2011

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

The convention in C# is that POCO classes should have a parameterless constructor, which can be expressed by the generic constraint where T : new(), and then be populated through setters, to make it easier to implement serialization.

So a very naive generic cache could look like this:

code:
public class Cache<T> where T : new()
{
   public Guid Save(T poco)
   {        
        var guid = new Guid();
        foreach(var field in typeof(T).GetFields(BindingFlags.Instance)
        {
             var fieldValue =  field.GetValue(poco, field.Name);
             saveSingleField(guid, field.Name, field.Value);            
         }
        return guid;
   }

   public T Load(Guid guid)
   {
        var poco = new T();
        foreach(var field in typeof(T).GetFields(BindingFlags.Instance)
        {
             var fieldValue = loadSingleField(guid, field.Name);
             field.SetValue(poco, fieldValue);
         }
         return poco;
    }
}
and in saveSingleField / loadSingleField you'd check whether the value is a primitive (save it directly), a collection (convert it to an array or dictionary, then save it directly) or another POCO (create or locate a cache for it).

What part of this basic design doesn't work for your use case?

Also, why can't you entrust to JSON.NET or another library the tricky work of serialization / deserialization, and just implement a string cache in your storage medium of choice?

NihilCredo fucked around with this message at 11:09 on Feb 10, 2019

EssOEss
Oct 23, 2006
128-bit approved

raminasi posted:

Well, beware; if you're using AutoMapper you probably shouldn't be using Automapper.

Fixed that for you.

AutoMapper is an abomination that saves you writing some lines of "this.X = that.X" code which 6 months down the line blossoms into a nice garden of "nobody knows how our data transfer objects get filled with data, sprinkle some .Ignore() or .Map() around if it doesn't feel right or just copy-paste one of those existing 50 line Mapping functions that does who knows what".

downout
Jul 6, 2009

NihilCredo posted:

The convention in C# is that POCO classes should have a parameterless constructor, which can be expressed by the generic constraint where T : new(), and then be populated through setters, to make it easier to implement serialization.

So a very naive generic cache could look like this:

code:
public class Cache<T> where T : new()
{
   public Guid Save(T poco)
   {        
        var guid = new Guid();
        foreach(var field in typeof(T).GetFields(BindingFlags.Instance)
        {
             var fieldValue =  field.GetValue(poco, field.Name);
             saveSingleField(guid, field.Name, field.Value);            
         }
        return guid;
   }

   public T Load(Guid guid)
   {
        var poco = new T();
        foreach(var field in typeof(T).GetFields(BindingFlags.Instance)
        {
             var fieldValue = loadSingleField(guid, field.Name);
             field.SetValue(poco, fieldValue);
         }
         return poco;
    }
}
and in saveSingleField / loadSingleField you'd check whether the value is a primitive (save it directly), a collection (convert it to an array or dictionary, then save it directly) or another POCO (create or locate a cache for it).

What part of this basic design doesn't work for your use case?

Also, why can't you entrust to JSON.NET or another library the tricky work of serialization / deserialization, and just implement a string cache in your storage medium of choice?

I wanted to initialize the cache when the application starts, so the way I preferred to try to do that was register a list of types to get with the cache library so it could create each cache type when needed as well as update when needed. And I'll look into string cache, that might work just fine. I hadn't considered it before.

B-Nasty
May 25, 2005

EssOEss posted:

AutoMapper is an abomination that saves you writing some lines of "this.X = that.X" code which 6 months down the line blossoms into a nice garden of "nobody knows how our data transfer objects get filled with data, sprinkle some .Ignore() or .Map() around if it doesn't feel right or just copy-paste one of those existing 50 line Mapping functions that does who knows what".

Pretty much exactly what I was going to write.

It's a cool, well-done library, and a neat idea, but once you move beyond a trivial project, the mapping configuration just becomes onerous and difficult to reason about. I know that any Jr. Developer can fix/change a sequence of manual x.foo=y.foo mappings, but they may screw up something in the mapping config that takes hours to track down.

Mr Shiny Pants
Nov 12, 2012
Isn't this pretty much all *magic* libraries? Even EF? All the poo poo that gets done under the hood makes stuff hard to reason about.

EssOEss
Oct 23, 2006
128-bit approved
I suppose it is more a question of balancing the cost and benefit. AutoMapper is a particularly outstanding example because the actual benefits are very minimal - I have yet to see a real world project where it would not be less effort to just write this.X = that.X type code. You don't really gain much at all to compensate for the mental overhead it adds.

Contrast this with EF, which does hide the SQL generation from you but pays you back with migrations, concurrency conflict handling, LINQ transformations (to a degree), change tracking and whatnot.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!
Okay this is a moon shot but maybe one of the three warlocks that works with the Microsoft Dynamic Language Runtime is reading this and can explain how the plumbing is supposed to work.

I am an idiot and have dabbled in created a domain-specific-language that I'm parsing with ANTLR4 using the Visual Studio extensions for it. The impression I got is to to use the ANTLR4 visitor to generate an abstract syntax tree using System.Linq.Expressions and friends. So my test statement turned into a root Expression containing other Expressions for the different math operations for a basic calculator test.

I think look at how I hook that up to the DLR and it's just layers and layers and layers and layers and layers containing stuff that seems to have existed before my program even launched and I can't figure it out. I see examples where things are compiled when they run when they compiled when they run and all these other illogical consistencies and loops. So I just kind of bullshitted this:

I create a ScriptRuntime for my language
I get ScriptEngine for my language from that ScripRuntime
I create a ScriptSource using that engine using a one-line string for my test program
...at this point, it's going to my LanguageContext.CompileSourceCode implementation, which is where I use my ANTLR4 stuff to make a System.Linq.Expression root node. I shove this into a ScriptCode subclass I created
...something inside the DLR engine internals knows to call the Run() method on my custom ScriptCode, which is where I run my expression as a lambda

Like, is that actually the right loving thing? Is that how this is going down? Am I getting anything at all from this? I kind of expected to have some kind of engine eating up the expressions that had fun features for me. At this point I might as well just make my own little interpreter. Or stop doing this an do something more worth the time like run in traffic with scissors.

If anybody's curious, I was seeing what it would take to make a little domain-specific language for NPC scripting in Unity. The commands I have to run deal with having them move here and there and say this and that without halting the entire game look real ugly using Unity's own stuff and IMO state machines are even worse for clarity. So I figured I'd throw a little bit of time at this little bit.

PS: Anything you might know about this before, like, 2016 is probably worthless. I've only found one book that was up-to-date and it decided to go to CrazyTown and I think ultimately just drop into IronPython just for shits and giggles. At this point I want to just call the Microsoft DLR "a library for embedding dynamic languages into IronPython."

Edit: If you're wondering what half of that has to do with being able to interpret code, so do I. I was kind of expecting to translate to some byte code format or something instead of LINQ Expressions, and I was expecting to feed that into something else. I assumed the DLR would give me some help marshalling .NET objects and a conduit for interactive debugging support; I vaguely recalled that being an advertising point. What I couldn't tell if there was any way to be able to preempt that engine to pause it and run stuff using cooperative multithreading, so this all might just be a dead end.

Rocko Bonaparte fucked around with this message at 08:31 on Feb 11, 2019

Mr Shiny Pants
Nov 12, 2012
Take a look at F#, it will do scripting and some metaprogramming.

Example: https://stackoverflow.com/questions/1156665/how-to-execute-f-code-found-in-a-string-in-a-compiled-f-program

You can run your whole shebang in a F# DLL and have it work as a scripting system.

Maybe I don't understand what you want. Probably :)

LOOK I AM A TURTLE
May 22, 2003

"I'm actually a tortoise."
Grimey Drawer

EssOEss posted:

I suppose it is more a question of balancing the cost and benefit. AutoMapper is a particularly outstanding example because the actual benefits are very minimal - I have yet to see a real world project where it would not be less effort to just write this.X = that.X type code. You don't really gain much at all to compensate for the mental overhead it adds.

I've used AutoMapper quite a bit, and while I think you're understating the benefits somewhat, I broadly agree and probably wouldn't use it in a future project. I actually think you're missing the most important argument against it, which in my opinion is the loss of type safety. The compiler can't stop you from writing Mapper.Map<Foo>(bar) even if there's no mapping from Bar to Foo, or from removing the Bar->Foo mapping code in the mistaken belief that it was unused.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

Mr Shiny Pants posted:

Take a look at F#, it will do scripting and some metaprogramming.

Example: https://stackoverflow.com/questions/1156665/how-to-execute-f-code-found-in-a-string-in-a-compiled-f-program

You can run your whole shebang in a F# DLL and have it work as a scripting system.

Maybe I don't understand what you want. Probably :)

I have some specific needs and not just to embed a scripting language. I don't know much at all about F# so I might as well just share. I basically need green threads and coroutines; preferably with the ability to stop the interpreter midway while running stuff and have it resume right there afterwards. It will presumably be rotating through different scripts. Whenever a script has a stopping point, I want it to stop right at that point in that particular routine, and not create a cascade of yields instead. Stuff like that is why I think I need at least my own syntax and then possibly my own interpreter too. I was kind of hoping to dodge the interpreter part by using the DLR. If I could insert instructions every X commands and before major loops then I could basically get green threads, but I don't know if anything the DLR or DLR-related languages is set up to be pre-empted like that. An interpreter meant for this kind of thing could just save its context as a frame somewhere and leave it alone. It would also let me save the progress of these scripts when saving the game because I could serialize the frames.

downout
Jul 6, 2009

I wanted to thank everyone for their suggestions.

My class construction isn't perfect, but it's close to working. One of my biggest problems was that in trying to extract it from an existing project, there was far too much tight coupling and legacy code that was initially pulled over into the library. So, I started reconstructing interfaces for every class and removing all unnecessary methods, etc. Go figure, there was a lot. Anyways, part of the original problems were caused by focusing too much on maintaining how it was working originally instead of the heavy refactor needed to remove all the unnecessary stuff.

A bunch of suggestions were hinting around at this, and offered good starting points at what to look.

leper khan
Dec 28, 2010
Honest to god thinks Half Life 2 is a bad game. But at least he likes Monster Hunter.

Rocko Bonaparte posted:

I have some specific needs and not just to embed a scripting language. I don't know much at all about F# so I might as well just share. I basically need green threads and coroutines; preferably with the ability to stop the interpreter midway while running stuff and have it resume right there afterwards. It will presumably be rotating through different scripts. Whenever a script has a stopping point, I want it to stop right at that point in that particular routine, and not create a cascade of yields instead. Stuff like that is why I think I need at least my own syntax and then possibly my own interpreter too. I was kind of hoping to dodge the interpreter part by using the DLR. If I could insert instructions every X commands and before major loops then I could basically get green threads, but I don't know if anything the DLR or DLR-related languages is set up to be pre-empted like that. An interpreter meant for this kind of thing could just save its context as a frame somewhere and leave it alone. It would also let me save the progress of these scripts when saving the game because I could serialize the frames.

Pull up. It sounds more like you’re inventing problems for a technical solution rather than solving for your fundamental complexity.

If your goal is to work on that technical problem, that’s fine; also Unity probably isn’t a good engine for that type of work. Especially without source access. Godot is more easily scripted in bespoke ways (check out gdnative) and will let you implement the hooks you’re actually looking for.

If you’re fine with losing some of the control of scheduling, you could model your execution environment with state machines. Building a simple FSM lib isn’t hard, but it can easily balloon into emulating some garbage pc architecture if you don’t control the scope. There are some paid unity libs that aren’t completely terrible for that.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

leper khan posted:

Pull up. It sounds more like you’re inventing problems for a technical solution rather than solving for your fundamental complexity.

If your goal is to work on that technical problem, that’s fine; also Unity probably isn’t a good engine for that type of work. Especially without source access. Godot is more easily scripted in bespoke ways (check out gdnative) and will let you implement the hooks you’re actually looking for.

If you’re fine with losing some of the control of scheduling, you could model your execution environment with state machines. Building a simple FSM lib isn’t hard, but it can easily balloon into emulating some garbage pc architecture if you don’t control the scope. There are some paid unity libs that aren’t completely terrible for that.
Well, the goal was to just take the DLR and beat it over the head. My hope with the Microsoft DLR was that there'd be an interpreter that could just take care of all this for me and I could, say, add a bytecode to save the current frames. There turned out to be an interpreter--in the IronPython project, of course, like apparently everything else that matters in the DLR--but it wasn't set up at all to suspend in that kind of way. It also has the architecture of an onion. I guess I could check again and maybe bust open the internal interpreter loop. I already did check awhile ago if I could use IronPython in Unity and it was totally possible. It can't use async--production IronPython is still 2.7, and async doesn't work in their IronPython 3 version, despite a patch I got from somebody at PyCon--so Python's own coroutine stuff isn't quite suitable. I think I would have wanted something more like Stackless Python's syntax anyways. It a built-in function to yell, "cut!" and just suspend right then and there. I was using that in a C++ engine I was dabbling in around 2010 or so and, well, that part worked really well. I was just dealing with the GIL about the time I put that down for other stuff.

For my personal stuff here, it wouldn't get rammed sideways into Unity but live outside and I'd just have it call into all my own code that generate dialogs, move NPCs, and whatnot. In effect, the scripts would be like little console programs that just make sure my main loop doesn't lock up when it prompts the user for a yes or a no or similar.

My problem with essentially creating a State Pattern framework is it's representation is still very disjoint from what I'm feeding it. Something where, say: move here, make a decision, yes path moves north, no path moves south. It's kind of intuitive for me to just write that out in a small script. If it's a state machine, usually that winds up with me drawing out all the circles and arrows on a sheet of paper that doesn't digitize, and then I'm converting that into State objects. So I'm two intermediate representations away from my original thoughts. You could then say I could work on some kind of state machine editor, but if I have to write that then I'm doing goofy poo poo and might as well just be doing that drat interpreter haha. I did see some assets in Unity that looked basically like state machine editors, but got all kinds of mixed reviews on them. They were all also behind paywalls so I'd have to fork over to find out if it even was worth it.

In my defense, dabbling in interpreters and language stuff has a secondary purpose: to cut my teeth on something I may want to transition into at my company professionally (not Unity related). It also has actually been enjoyable--at least for the hour or so I blew making the dumbest stack-based interpreter. I blew a bunch of time trying to dodge the interpreter problem but it's naively simple--or it's just that I've blown a lot of my career looking at and working with managed code that this doesn't bother me (yet). Or maybe I should do something more interesting than just the calculator stuff and then report back.

I still think it's funny how I remembered my first assignment in my computer architecture class having to do with various machine language syntaxes, and I remember doing the stack-based assignments while thinking it was the biggest bullshit ever. I've ultimately done as much or more in that kind of syntax than I have done with assembler professionally.

Where's that guy that does all the stuff in Forth? He's going to pop out of nowhere after this post, right?

Munkeymon
Aug 14, 2003

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



code:
System.Data.Entity.Core.MetadataException
  HResult=0x80131939
  Message=Schema specified is not valid. Errors: 
The mapping of CLR type to EDM type is ambiguous because multiple CLR types match the EDM type 'Action'. Previously found CLR type 'Company.Domain.Enums.Action', newly found CLR type 'System.Data.Entity.Core.SchemaObjectModel.Action'.
:shuckyes: and all the advice I found on how to point it at the right type didn't work so that's cool.

Hey, at least they fixed it in the next version. Accidentally.

Rocko Bonaparte
Mar 12, 2002

Every day is Friday!

Munkeymon posted:

code:
System.Data.Entity.Core.MetadataException
  HResult=0x80131939
  Message=Schema specified is not valid. Errors: 
The mapping of CLR type to EDM type is ambiguous because multiple CLR types match the EDM type 'Action'. Previously found CLR type 'Company.Domain.Enums.Action', newly found CLR type 'System.Data.Entity.Core.SchemaObjectModel.Action'.
:shuckyes: and all the advice I found on how to point it at the right type didn't work so that's cool.

Hey, at least they fixed it in the next version. Accidentally.

Wow isn't "Action" pretty drat overloaded in the .NET ecosystem? I'm not surprised...

Munkeymon
Aug 14, 2003

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



Rocko Bonaparte posted:

Wow isn't "Action" pretty drat overloaded in the .NET ecosystem? I'm not surprised...

I caused it by referencing an enum in Data.Entity in an EF entity. Otherwise it wouldn't have looked in that assembly for names. Still pretty goddamn annoying that nobody ever thought to add a standard attribute to qualify type names.

LongSack
Jan 17, 2003

I’ve decided to add scripting to my firewall management app. The reason is that there are things I need to know about a firewall to let me generate rule scripts, create NATs, etc. Basically, I need to know whether a firewall is internet-facing or customer-facing.

90% of the firewalls I manage follow standard naming conventions which I could normally use (i.e, a firewall is internet-facing if it has an interface named ‘extpop’), but the others were acquired as the result of an acquisition. I could hard-code the rules, but that leaves me exposed for the next acquisition. So rather than code that in the app, I’m adding in the ability to execute a script in the factory method that creates a firewall object just before it returns.

I’m using C# as my scripting language, because it is surprisingly easy to implement. I’m caching my compiled assemblies, because there is a noticeable overhead for compilation.

My question is, once I’m done loading all the firewalls, should I “unload” the cached assemblies? And if so, how? Would clearing the cache dictionary be sufficient or is there something more I should do?

brap
Aug 23, 2004

Grimey Drawer
IIRC executable code will not get GC’ed, so I don’t think clearing a dictionary is enough.

You should load the generated assemblies in their own app domain and then destroy the app domain when you’re done.

LongSack
Jan 17, 2003

brap posted:

IIRC executable code will not get GC’ed, so I don’t think clearing a dictionary is enough.

You should load the generated assemblies in their own app domain and then destroy the app domain when you’re done.

Thanks for the response. I wasn’t able to find any docs that said whether assemblies are collected, but the whole thing has become moot. Originally, all the firewall models were built at program start, and that was that, so the scripts weren’t needed after.

Today I added a “Reload” button, so I think I’ll keep the cached assemblies around.

LongSack
Jan 17, 2003

Am I the only one that finds it odd that you have to overload the '>=' and '<=' operators explicitly even if you overload '==', '<' and '>' already? Is there a reason for this besides "that's just the way it is"?

Potassium Problems
Sep 28, 2001

LongSack posted:

Am I the only one that finds it odd that you have to overload the '>=' and '<=' operators explicitly even if you overload '==', '<' and '>' already? Is there a reason for this besides "that's just the way it is"?

I would imagine under the hood they are distinctly different operators (Equals, LessThan, LessThanOrEqualTo, GreaterThan ...) and are handled as such, and the `>=` & `<=` operators aren't just syntactic sugar, if that makes sense.

I would find it odd you're overloading operators in the first place :v:

B-Nasty
May 25, 2005

LongSack posted:

Am I the only one that finds it odd that you have to overload the '>=' and '<=' operators explicitly even if you overload '==', '<' and '>' already? Is there a reason for this besides "that's just the way it is"?

That's just the way it is. The whole equality thing is a mess, but at least with those operators, you can implement CompareTo(x, y) and then all of the operators are exactly the same compared to zero. e.g.

public static bool operator <(MyType x, MyType y) => CompareTo(x, y) < 0;
public static bool operator <=(MyType x, MyType y) => CompareTo(x, y) <= 0;
public static bool operator >(MyType x, MyType y) => CompareTo(x, y) > 0;
...etc

LongSack
Jan 17, 2003

B-Nasty posted:

That's just the way it is. The whole equality thing is a mess, but at least with those operators, you can implement CompareTo(x, y) and then all of the operators are exactly the same compared to zero. e.g.

public static bool operator <(MyType x, MyType y) => CompareTo(x, y) < 0;
public static bool operator <=(MyType x, MyType y) => CompareTo(x, y) <= 0;
public static bool operator >(MyType x, MyType y) => CompareTo(x, y) > 0;
...etc

Yeah, that’s how I implement them.

Potassium Problems posted:

I would find it odd you're overloading operators in the first place :v:

Why is that surprising? Two Account objects are equal if they have the same AccountId whether or not the two objects are the same reference. And IComparable<T>, where it makes sense, is something like
C# code:
public int CompareTo(Firewall other) => Name.CompareTo(other.Name);

B-Nasty
May 25, 2005

LongSack posted:

Why is that surprising? Two Account objects are equal if they have the same AccountId whether or not the two objects are the same reference.

You might want to be careful with doing something like this. It's an anti-pattern to implement equality operators based around something like a database primary key if there are other properties on the object that can be not-equal. For example, if you have X and Y with the same ID, but you update the AccountName on object Y, is it still accurate to say X.Equals(Y)==true? If you care about the IDs, just do X.AccountId==Y.AccountId so that you intent isn't hidden.

NihilCredo
Jun 6, 2011

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

Does c# let you mark a class as NotEquatable so that attempting to use == on it will be a compile-time error (instead of defaulting to reference equality)?

If that's not possible, better to at least have a consistent convention for object equality.

Munkeymon
Aug 14, 2003

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



I think you could override the equality operators to throw and mark them deprecated so you'd get a compiler warning but that's not quite what you want.

User0015
Nov 24, 2007

Please don't talk about your sexuality unless it serves the ~narrative~!
Phone posting here, but what happens to events when a using statement completes? For example

Using(var foo = new Foo()) {
...
foo.workCompleteZugZug += SomeHandler(...);
await foo.DoWork();
}

Once that completes, does that clean up properly on dispose? I'm wondering if that leaves behind a reference somewhere.

Potassium Problems
Sep 28, 2001

LongSack posted:

Why is that surprising? Two Account objects are equal if they have the same AccountId whether or not the two objects are the same reference. And IComparable<T>, where it makes sense, is something like
C# code:
public int CompareTo(Firewall other) => Name.CompareTo(other.Name);

Like B-Nasty said, it easily leads to unclear code, and usually other devs (maybe even yourself, 6 months down the line) will have to dig into the overload code to see what the intent really is. I've seen really obtuse stuff from people who thought they were being super clever. Sanitized example:

code:
StatWeight weight = GetWeight();

if(weight <= 0.75) 
{
  ...
}
There's an overload and an implicit operator at work here, and it makes it extremely hard to reason about the code w/o knowing the details of how a StatWeight can become a double (or is it a float? decimal maybe?) and how it's performing its operations. Tread carefully!

Potassium Problems fucked around with this message at 21:27 on Feb 27, 2019

Adbot
ADBOT LOVES YOU

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings

User0015 posted:

Phone posting here, but what happens to events when a using statement completes? For example

Using(var foo = new Foo()) {
...
foo.workCompleteZugZug += SomeHandler(...);
await foo.DoWork();
}

Once that completes, does that clean up properly on dispose? I'm wondering if that leaves behind a reference somewhere.

IIRC, that will keep foo from being GC'd.

Though I'd feel like it wouldn't be the same issue if you just treated SomeHandler as an Action that Foo takes in its ctor.

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