In Which @Captain asks C# Beginner Questions


  • Considered Harmful

    @Magus said in In Which @Captain asks C# Beginner Questions:

    @error said in In Which @Captain asks C# Beginner Questions:

    But at least I don't need to worry that they assumed this field was a variable because the two were indistinguishable from one another.

    But not because you thought of a name, simply because you decided to take the easy way out and be sloppy.

    Please, tell me one name for a field that no one would dare use for a variable. Just one.



  • @error said in In Which @Captain asks C# Beginner Questions:

    If I can't tell what Foo method does because it depends on _foo field, which is totally inaccessible to me, that's a shit design.

    No, that's encapsulation. I think you're mistaking "can't tell what it does" in terms of contract vs. "can't tell what it does" in terms of implementation.

    I can't tell what Stream.Open() (or however you do it, too lazy to check) does in terms of implementation. Does the stream have some kind of pointer into the data? How is that pointer stored? How does the stream know when to fetch data from disk, and how? All that stuff would be stored in private fields, and if you want to implement your TurboStream you'd be overriding methods wholesale, not hooking in the middle of a process you're not supposed to know about and which can change on the whim.

    (A better example would probably be some sort of "fetch next 20 characters" method - it very much depends on some internal pointer into the stream, but that pointer doesn't have to be accessible to you - all that's accessible to you is a method which has a clearly defined contract of giving you next 20 characters. There might not even be a pointer at all, and you'd be none the wiser, and that's good.)

    I do, however, know that it opens a stream, and I can then read from that stream. That's contract, and I don't need to know what tricks the Stream class uses under the hood to achieve that, and I only need to know a very limited part of it to plug in my own behavior.

    So yeah, classes keep internal state because they have to so that the publicly exposed API is clear. A completely-stateless Stream class would likely demand from the user to juggle all those implementation details themselves, which we a) don't want to encumber them with and b) don't want to have them rely on, because they can change.


  • Considered Harmful

    No, I totally get that some objects are inherently stateful. The trick is isolation. Streams have a very narrowly defined scope. If providing a Stream interface is not central to your class' purpose, then move that shit into a separate class. Encapsulate it by implementing an interface and exposing that. Hide which concrete implementation you return.

    Good is having the state of your FooStream private. Better is not having to tell anyone that FooStream even exists.



  • @error said in In Which @Captain asks C# Beginner Questions:

    Streams have a very narrowly defined scope. If providing a Stream interface is not central to your class' purpose, then move that shit into a separate class

    Which doesn't change the amount of private state your class has, it just shifts the responsibility. Someone's gonna be keeping that pointer, whether it's your class or the Stream you refactored out of it and which is now your class' private state.

    Of course breaking components into subcomponents is good, duh - but it still means your Foo() method uses some fields that are (even more) inaccessible to you. It's just using it via the Stream class.


  • Considered Harmful

    @Maciejasjmj said in In Which @Captain asks C# Beginner Questions:

    Of course breaking components into subcomponents is good, duh - but it still means your Foo() method uses some fields that are (even more) inaccessible to you. It's just using it via the Stream class.

    Which is fine. The goal wasn't to eliminate state. It was to only access state through encapsulated channels (public members and interfaces). Your Stream implementation is tucked away in FooStream, and all the world sees is IStream, and that's how you get at it. No one sees your implementation details, you have a layer of insulation from changes, and your class doesn't need to fiddle around hidden fields to accomplish it.

    I was never against having or using fields. That's a ridiculous strawman.



  • @error said in In Which @Captain asks C# Beginner Questions:

    I was never against having or using fields. That's a ridiculous strawman.

    You had an issue with not being able to tell what a method does because it relies on a piece of state inaccessible to you. That's how encapsulation works - whether it's tucked behind an interface or not.

    Interfaces just make it even more explicit that you don't know what Foo() or Stream.Open() or whatever does - not only don't you know the fields upon which Foo()'s behavior relies, you don't even know what code gets executed when you do call Foo().

    @error said in In Which @Captain asks C# Beginner Questions:

    you have a layer of insulation from changes, and your class doesn't need to fiddle around hidden fields to accomplish it.

    Your class still does need to fiddle around its own hidden fields. Your FooStream still needs to keep a pointer and your FooFileParser still needs to maintain its IStream instance which it doesn't expose to the user. You can keep piling up turtles, but the end result is that you're gonna have that private piece of state no upper layer will be able to get at somewhere.


  • Considered Harmful

    @Maciejasjmj said in In Which @Captain asks C# Beginner Questions:

    @error said in In Which @Captain asks C# Beginner Questions:

    I was never against having or using fields. That's a ridiculous strawman.

    You had an issue with not being able to tell what a method does because it relies on a piece of state inaccessible to you. That's how encapsulation works - whether it's tucked behind an interface or not.

    Interfaces just make it even more explicit that you don't know what Foo() or Stream.Open() or whatever does - not only don't you know the fields upon which Foo()'s behavior relies, you don't even know what code gets executed when you do call Foo().

    @error said in In Which @Captain asks C# Beginner Questions:

    you have a layer of insulation from changes, and your class doesn't need to fiddle around hidden fields to accomplish it.

    Your class still does need to fiddle around its own hidden fields. Your FooStream still needs to keep a pointer and your FooFileParser still needs to maintain its IStream instance which it doesn't expose to the user. You can keep piling up turtles, but the end result is that you're gonna have that private piece of state no upper layer will be able to get at somewhere.

    See my earlier post. 1 was consider if you really need state. 2 was move the state into another class. 3 was minimize the surface area. I never said eliminate private state. That's obviously impossible.

    I've always been arguing for more encapsulation here.



  • @error Oh the "sloppy" practices argument, please :rolleyes:

    If the field is private or you mark the class sealed they can't inherit it. Sorry you seem to be arguing for naming conventions to enforce your OO design. Which is nonsense.


  • Considered Harmful

    @lucas1 said in In Which @Captain asks C# Beginner Questions:

    Sorry you seem to be arguing for naming conventions to enforce your OO design. Which is nonsense.

    That doesn't even make sense. A naming convention can't enforce anything, just distinguish between two different kinds of things (members and locals, in this case).



  • @error You advocated that.


  • Considered Harmful

    @lucas1 said in In Which @Captain asks C# Beginner Questions:

    @error You advocated that.

    A different naming convention for fields than variables? Yes, I did and I do. You never want to confuse those two things.



  • The compiler will flag that straight away if the var is in scope. Sorry you keep on trying to advocate shite design and fixing it with strict adherence to naming convention.

    This is total bollox.



  • At the end of the day, the compiler should have warned about shadowing. The naming convention is supposed to help stop that bug from happening, since the compiler kind of sucks and is more weakly typed than Haskell for example. Let's move on.





  • @Captain Anyway, as for the extensibility tools:



  • @Captain I've done it!

    using System.Collections.Immutable;
    using System.Linq;
    using Microsoft.CodeAnalysis;
    using Microsoft.CodeAnalysis.CSharp;
    using Microsoft.CodeAnalysis.CSharp.Syntax;
    using Microsoft.CodeAnalysis.Diagnostics;
    
    namespace ShadowFinder
    {
    	[DiagnosticAnalyzer(LanguageNames.CSharp)]
    	public class ShadowFinderAnalyzer : DiagnosticAnalyzer
    	{
    		public const string DiagnosticId = "ShadowFinder";
    
    		// You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat.
    		// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Localizing%20Analyzers.md for more on localization
    		private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.AnalyzerTitle), Resources.ResourceManager, typeof(Resources));
    		private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.AnalyzerMessageFormat), Resources.ResourceManager, typeof(Resources));
    		private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.AnalyzerDescription), Resources.ResourceManager, typeof(Resources));
    		private const string Category = "Naming";
    
    		private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning, isEnabledByDefault: true, description: Description);
    
    		public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
    
    		public override void Initialize(AnalysisContext context)
    		{
    			context.RegisterSyntaxNodeAction(AnalyzeLocal, SyntaxKind.LocalDeclarationStatement);
    		}
    
    		private void AnalyzeLocal(SyntaxNodeAnalysisContext context)
    		{
    			var semanticModel = context.SemanticModel;
    			var localDeclaration = (LocalDeclarationStatementSyntax)context.Node;
    			var localDataFlowAnalysis = semanticModel.AnalyzeDataFlow(localDeclaration);
    
    			var declared =
    				localDeclaration
    					.Declaration
    					.Variables
    					.Select(variable => semanticModel.GetDeclaredSymbol(variable))
    					.Where(symbol => !localDataFlowAnalysis.WrittenOutside.Contains(symbol))
    					.OfType<ILocalSymbol>();
    
    			var root = context.Node.Parent;
    			while (root.Parent != null)
    				root = root.Parent;
    
    			var fields =
    				root
    					.DescendantNodes()
    					.OfType<NamespaceDeclarationSyntax>()
    					.SelectMany(ns => ns.DescendantNodes().OfType<TypeDeclarationSyntax>())
    					.SelectMany(outer => outer.DescendantNodes())
    					.OfType<FieldDeclarationSyntax>()
    					.SelectMany(declaration => declaration.Declaration.Variables.Select(variable => semanticModel.GetDeclaredSymbol(variable)))
    					.OfType<IFieldSymbol>();
    
    			if(fields.Any(field => declared.Any(local => local.Name == field.Name)))
    				context.ReportDiagnostic(Diagnostic.Create(Rule, context.Node.GetLocation()));
    		}
    	}
    }
    

    Create a syntax analyzer project, use this, and make yourself a vsix, and you'll have a warning for shadowed types! But make sure to fix the resx. Should be clear enough how to extend it to parameters. Also this is kind of buggy since it doesn't get the class that directly contains the local, just any in the file, but this was a quick first ever syntax analyzer.


  • Considered Harmful

    @Magus said in In Which @Captain asks C# Beginner Questions:

    DiagnosticAnalyzer

    Upvote because cool, but you may be wearing the complicator's gloves.



  • @error Nah. This fixes the tooling with information we have at hand. And you only have to do it once, after which you can just hand people the vsix. Resharper looks less and less reasonable the more I learn how to use this stuff.



  • I have a HashSet of strings. I want to "modify" each of the strings so that all of the periods are removed. If this was Haskell, I'd do

     fmap (filter (!= '.')) theHashSet
    

    What's the C#/Linq equivalent? (This, and filling in some configuration fields, is the last subtask for this godforsaken task. W00p.)



  • @Captain Is this thread still going on? You should have a thread for intermediate questions by now


  • kills Dumbledore

    @Captain linqkwould be (on mobile so forgive any formatting errors)

    Var noDots = from s in strings
    Select s.replace(".","");
    


  • This is a baby question, though.


  • kills Dumbledore

    @Jaloopa or in non sql style,

    Strings.select(s=>s.replace(".",""));



  • @Jaloopa Yeah, that totally worked once I fixed the formatting. Thanks.



  • @Captain Just remember to pick up all the periods so they don't fall out of the CPU and rattle-around in the bottom of the case.



  • @Jaloopa It looks like => is acting like a lambda quantifier. Does it always work that way?



  • @Captain said in In Which @Captain asks C# Beginner Questions:

    It looks like => is acting like a lambda quantifier. Does it always work that way?

    Yeah. Thought that would be the first thing you'll be checking out.

    x => doStuff(x) is either a functor object you can call, or an expression tree object you can parse (and compile to an equivalent functor), depending on the context.



  • @Captain It's literally called the lambda operator: https://msdn.microsoft.com/en-us/library/bb311046.aspx

    Surprised you hadn't come across it before with all the Haskell love.



  • @blakeyrat I already learned that lesson when I filtered out numbers. You have no idea how many ints there really are until you try to pick them up all by yourself.



  • @Maciejasjmj said in In Which @Captain asks C# Beginner Questions:

    x => doStuff(x) is either a functor object you can call, or an expression tree object you can parse (and compile to an equivalent functor), depending on the context.

    However, if you ever have an Action<T> that can take x => DoStuff(x) normally you'd do Action<T> doStuff = DoStuff;, because the method group matches the pattern.

    Similarly, you can do strange things like var atLeastOneHasAThing = strings.Select(string.IsNullOrWhitespace).Any(x => !x);



  • However, if you ever have an Action<T> that can take x => DoStuff(x) normally you'd do Action<T> doStuff = DoStuff;, because the method group matches the pattern.

    So do lambda expressions have a different type than "regular" functions?

    In Haskell, all functions are going to have a type of the form (a -> b). So, for example, the only difference between

    ex1 :: Int -> String
    ex1 = \x -> show x
    
    ex2 :: Int -> String
    ex2 x = show x
    
    ex3 :: Int -> String
    ex3 = show
    

    is the implementation. So it's like a big "duh" that you can pass any of them anywhere a function with that type is expected.

    Is it like that in C#? I definitely did not assume so, but I would love to be pleasantly surprised.
    .

    var atLeastOneHasAThing = strings.Select(string.IsNullOrWhitespace).Any(x => !x);

    What's strange about that?



  • @Captain I don't know if you'd call it "strange", but he's selecting a collection of bools (one for each string; if the string is null or is whitespace, it's true otherwise false), then running Any on the negation of the bools. (Meaning the first false string will satisfy the Any condition and the entire statement will return true.)

    It's a really shitty and confusing way of writing:

    var atLeastOne = strings.Any( x => !x.IsNullOrWhitespace() );
    

    BTW I'm sure Haskell's the same way or whatever, but in C# all LINQ statements are deferred execution, so the utterly unnecessary Select in Magus' code example won't actually create the entire set of bools all at once, it'll do them one-at-a-time as his Any statement requires them.


  • Discourse touched me in a no-no place

    @blakeyrat said in In Which @Captain asks C# Beginner Questions:

    all LINQ statements are deferred execution

    Absolutely everything in Haskell is deferred execution if at all possible; it's the big feature of the language.



  • @dkf

    @blakeyrat said in In Which @Captain asks C# Beginner Questions:

    I'm sure Haskell's the same way or whatever,



  • @Captain basically, there is a thing called a delegate in c#, which acts as a very strongly typed function pointer. Defined correctly, a delegate can take either a raw method name (what I was trying to demonstrate) or a lambda.



  • OK! Time for a new question. This time it's about building an enumerated type, where each value can (or, perhaps optionally, must) have specific properties.

    In Haskell, I'd do something like

    data Color = RGB { r :: Int 
                     , g :: Int 
                     , b :: Int
                     }
               | CYMK { c :: Int I
                      , y :: Int 
                      , m :: Int
                      , k :: Int
                      }
    

    You can verbally read this as "A color can be an RGB value, which has three sub-values named r, g, and b or a CYMK value..."

    Do I really have to make a bunch of classes and do inheritance to implement something similar? That's my first thought, anyway. Is there a better way?



  • @Captain How specific is this example? Because the framework already has a Color class you should be using. If it's about colors.



  • @blakeyrat I'm just trying to show the "enum of structs" data structure that I'd like to implement and use.

    The problem I'd like to solve is:

    1. disjoint types
    2. that can all be converted to a canonical representation
    

    I guess I went with an interface.



  • @Captain said in In Which @Captain asks C# Beginner Questions:

    @blakeyrat I'm just trying to show the "enum of structs" data structure that I'd like to implement and use.

    No, in C# the values behind an enum can only be of an integral type.

    You will have to use inheritance to get something similar to your example.



  • @Captain Well you could make ad-hoc/anonymous objects (assuming you're using a modern version of C#), but I wouldn't recommend it if the objects you're making have an actual concrete reason to be a type or struct, which in this case they do.

    Also "enum" in C# specifically refers to enumerable lists with integer indexes, so that's not what you want.


  • Java Dev

    @Captain I'd call that a tagged union, not an enumeration. Enumerations only have the tags.



  • @blakeyrat said in In Which @Captain asks C# Beginner Questions:

    Also "enum" in C# specifically refers to enumerable lists with integer indexes, so that's not what you want.

    Fair enough, thanks. I'll be sure not to confuse the Haskell notion (which is, basically, just "recursive enumerability").

    @PleegWat said in In Which @Captain asks C# Beginner Questions:

    I'd call that a tagged union, not an enumeration. Enumerations only have the tags.

    Good point about the "tagged union". That's definitely an accurate way to describe what I want.



  • @Captain To expand on Blakey's comment:

    var color = new
    {
      R = 255,
      G = 23,
      B = 23
    }
    

    will compile, but you can't return it, because the type name doesn't exist until compile time. They're thinking the next version of C# may allow anonymous returns, but it could really get messy fast imo.

    But yeah, if you need some things in a better type for a short amount of time, it's a good strategy. Like you might be parsing some XML:

    var colors =
      document
        .Descendants
        .Where(element => element.Name == "Color")
        .Select(element =>
          new
          {
            R = element.Attributes.FirstOrDefault(attribute => attribute.Name == "Red") ?? 0,
            G = element.Attributes.FirstOrDefault(attribute => attribute.Name == "Green") ?? 0,
            B = element.Attributes.FirstOrDefault(attribute => attribute.Name == "Blue") ?? 0
          }
        );
    

    Is that anything like what you want to know?



  • Changing topics briefly, is Linq smart enough to parse custom tags in xml? And is there a "standard" data type for xml values?



  • @Captain Yes. System.Xml.Linq's XDocument and its children are what you want. The old XML parsing systems in C# are kind of awful.

    Basically, everything is an XNode, most of which are XElements and XAttributes. All of these have Name properties of type XName, which is assignable directly with a string, but you can also concatenate an XNamespace with a string to get a namespaced element. You'll probably need to do a lot of that.

    It's all pretty simple to deal with ultimately, only the XNamespace is really a gotcha.



  • @Captain https://dotnetfiddle.net/IQYP5j

    This will give you a clearer idea of how LINQ to XML works. Be careful I have created monstrosities of Linq queries using these methods on complicated xml structures.

    If you want to serialize between an Object and XML and vice versa you can do something like the following here.



  • OK Question time!!

    What do people use for "common" domain objects, like US street addresses and states and weights and measures? And ISO codes and the like?

    Ideally, I want something that plays nice with Entity Framework (I'm not sure if not playing nice with EF is a thing to worry about, though)



  • @Captain string?



  • @groo :vomit:

    So there isn't a data type that represent valid ISO country codes, for example? And can parse and render them as strings? I don't want to mix up different kinds of strings...



  • @Captain said in In Which @Captain asks C# Beginner Questions:

    like US street addresses

    There is an address object in the framework somewhere, a pretty good one afaik.

    ...but ultimately, addresses can be almost anything, so string is probably safer.

    @Captain said in In Which @Captain asks C# Beginner Questions:

    So there isn't a data type that represent valid ISO country codes, for example? And can parse and render them as strings? I don't want to mix up different kinds of strings...

    If there isn't, you can make one, or use an enum, since those have methods to parse them.


Log in to reply