Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. The Lounge
  3. c# Const vs. Readonly, Scope, Usings

c# Const vs. Readonly, Scope, Usings

Scheduled Pinned Locked Moved The Lounge
csharpdatabasevisual-studiobusinessjson
44 Posts 17 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • P pherschel

    I can't do this either :(( const DateTime tick = new DateTime(2001, 1, 1);

    - Pete

    F Offline
    F Offline
    Fabio Franco
    wrote on last edited by
    #23

    That's because const value must be known at compile-time. Instantiating DateTime requires code execution, which will only be done at run-time. For that scenario you have the readonly, a run-time constant (intialized once and never changes). You can only use const on variables that can be represented by literals.

    To alcohol! The cause of, and solution to, all of life's problems - Homer Simpson ---- Our heads are round so our thoughts can change direction - Francis Picabia

    1 Reply Last reply
    0
    • D dandy72

      pherschel wrote:

      How about a free keyword on a variable or something to get me out of the business of resource management

      Isn't that a contradiction in terms? Having done over a decade of C++ (and not missing it one bit), it seems to me that if you want a 'free' keyword, then you're not getting out of the business of resource management, you're asking to get into it... I must be completely misunderstanding something.

      K Offline
      K Offline
      kalberts
      wrote on last edited by
      #24

      In my historical archives I have preserved a long NetNews-discussion from the late 1990s: Even that late, there were some people insisting that high level languages are just a short-lived fad; programmers will soonrealize that for high-performing programs, you must code in assembler. Fortunately, they were not right. For another fifteen years, I was similarly convinced that to control memory use, you simply had to do mallocs and frees yourself in longhand. No way can an automatic garbage collector know what is the best strategy. Then I read the memory management chapter of "CLR via C#". Again and again, I said to myself: Gee, that's smart! I would never have thought of that! ... So my sceptisism towards garbage collectors dwindled away in a few days. I have several colleagues who have not yet read the same book, so they are still as sceptical as I used to be. Besides, some of them are coding in C rather than C#. If/when you switch from C to C#, you might want to bring some old habits over to the new environment, such as trying to "help" the GC. You should not. Its usefulness is at the same level as telling the C compiler how to use its registers. (I recently read that 'register' will disappear from C++17 - I'd say that is extremely overdue.)

      D 1 Reply Last reply
      0
      • K kalberts

        In my historical archives I have preserved a long NetNews-discussion from the late 1990s: Even that late, there were some people insisting that high level languages are just a short-lived fad; programmers will soonrealize that for high-performing programs, you must code in assembler. Fortunately, they were not right. For another fifteen years, I was similarly convinced that to control memory use, you simply had to do mallocs and frees yourself in longhand. No way can an automatic garbage collector know what is the best strategy. Then I read the memory management chapter of "CLR via C#". Again and again, I said to myself: Gee, that's smart! I would never have thought of that! ... So my sceptisism towards garbage collectors dwindled away in a few days. I have several colleagues who have not yet read the same book, so they are still as sceptical as I used to be. Besides, some of them are coding in C rather than C#. If/when you switch from C to C#, you might want to bring some old habits over to the new environment, such as trying to "help" the GC. You should not. Its usefulness is at the same level as telling the C compiler how to use its registers. (I recently read that 'register' will disappear from C++17 - I'd say that is extremely overdue.)

        D Offline
        D Offline
        dandy72
        wrote on last edited by
        #25

        Member 7989122 wrote:

        If/when you switch from C to C#, you might want to bring some old habits over to the new environment, such as trying to "help" the GC. You should not.

        Well said. When I transitioned from C++ to C#, I read a small amount of material on garbage collection (admittedly, a very small amount), but everything I read convinced me from the get-go that it was pointless to try to do just that, and instead just let it do its job. I've never looked back.

        1 Reply Last reply
        0
        • T TheGreatAndPowerfulOz

          dandy72 wrote:

          I must be completely misunderstanding something.

          Or he is...

          #SupportHeForShe Government can give you nothing but what it takes from somebody else. A government big enough to give you everything you want is big enough to take everything you've got, including your freedom.-Ezra Taft Benson You must accept 1 of 2 basic premises: Either we are alone in the universe or we are not alone. Either way, the implications are staggering!-Wernher von Braun

          D Offline
          D Offline
          dandy72
          wrote on last edited by
          #26

          :laugh: I always give the other guy the benefit of the doubt. Then if it turns out I'm right, I point and laugh.

          1 Reply Last reply
          0
          • F Fabio Franco

            TheGreatAndPowerfulOz wrote:

            The fact the compiler writers did it for string means they could have done it for any object.

            That's not true, although string is an object, it's a special type of object. It's immutable and can be allocated both on the heap and the stack. Other object types are allocated on the heap exclusively but are not nativelly immutable. Strings also have a mechanism called interning. By default const strings are interned for optimization purposes. Having that said, string gets all kinds of special treatment. Remember that you can only declare a const string literal. For example:

            const string _myString = String.Empty; //Compile-time error.

            This also defeats the statement that other reference types could have the same treatment. As every reference type you want to use const with, would have to have a literal representation, so its value could be determined at compile-time. Like the string literal.

            TheGreatAndPowerfulOz wrote:

            readonly is used for objects (except for _string_ :rolleyes: )

            Value types can also be readonly. Reinforcing what Original Griff said, the difference between readonly and const are as simple as one is a run-time constant and the other a compile-time constant. If a variable's value cannot be determined at compile-time, it cannot be a const.

            To alcohol! The cause of, and solution to, all of life's problems - Homer Simpson ---- Our heads are round so our thoughts can change direction - Francis Picabia

            T Offline
            T Offline
            TheGreatAndPowerfulOz
            wrote on last edited by
            #27

            What you say may be true, but _const_ could have and should have been used for both situations.

            #SupportHeForShe Government can give you nothing but what it takes from somebody else. A government big enough to give you everything you want is big enough to take everything you've got, including your freedom.-Ezra Taft Benson You must accept 1 of 2 basic premises: Either we are alone in the universe or we are not alone. Either way, the implications are staggering!-Wernher von Braun

            F 1 Reply Last reply
            0
            • T TheGreatAndPowerfulOz

              What you say may be true, but _const_ could have and should have been used for both situations.

              #SupportHeForShe Government can give you nothing but what it takes from somebody else. A government big enough to give you everything you want is big enough to take everything you've got, including your freedom.-Ezra Taft Benson You must accept 1 of 2 basic premises: Either we are alone in the universe or we are not alone. Either way, the implications are staggering!-Wernher von Braun

              F Offline
              F Offline
              Fabio Franco
              wrote on last edited by
              #28

              TheGreatAndPowerfulOz wrote:

              could

              Yes, could, but should it? They behave differently and actually generate different IL code. The compiler could also determine that automatically, when generating IL code. But I don't think that's a good idea, but we will end up in another filosophical discussion. In my opinion this compiler behavior could generate those situations developers don't understand what's happening and why their code does not work as expected.

              To alcohol! The cause of, and solution to, all of life's problems - Homer Simpson ---- Our heads are round so our thoughts can change direction - Francis Picabia

              T U 2 Replies Last reply
              0
              • P pherschel

                With C# getting to version 7+ I wish I could have some basic improvments. Is it me or do you get confused by this? Why can't I say

                const DateTime today = DateTime.Now;

                I can see readonly for parameters and such, but I would be happy using const there too

                void Doit(const MyObj arg) ...

                For properties, why can't I hide the worker variable for the rest of the class?

                public int PageNbr
                {
                int _worker = 9;

                get { return _worker;}
                set { _worker = value; }
                }

                For destructors, why can't you give me option to destroy right away? I hate disposing with all its using code bloat. How about a free keyword on a variable or something to get me out of the business of resource management. If you open a file and a DB you have to nest usings before you even get started doing some work! Or maybe I'm missing something?

                - Pete

                I Offline
                I Offline
                irneb
                wrote on last edited by
                #29

                What's so wrong with it? If you don't like the idea, you could always just:

                var someObject = new MyDisposableObject();
                // Do some work with it
                someObject.Dispose();

                The trouble with that is, if the code never reaches that Dispose line, it won't get called. In which case you then incorporate it into a try-finally block like so:

                var someObject = new MyDisposableObject();
                try {
                // Do some work with it
                }
                finally {
                someObject.Dispose();
                }

                But ... that's exactly what a using clause does for you ... just a whole lot less coding on your side:

                using (var someObject = new MyDisposableObject()) {
                // Do some work with it
                }

                I might have liked an idea where you could add numerous unrelated disposables into the using clause - this may alleviate nesting usings. Though take note that with nesting you've got some control over the order in which they're disposed. Also some disposables are contained in other disposables, and in most such cases disposing the container does so to its contents as well, meaning you only need dispose the final container --> only one using clause. More of an issue for me is the fact that a destructor isn't called deterministically, if at all. If such were possible, then the dispose pattern could be moved into the destructor instead of a dispose method. And even if you forget to dispose it, it would finally happen once the GC frees it from resources. Though this means the entire GC idea needs a revamp, in fact some of C#'s creators also think this is one of the biggest mistakes in the entire DotNet, just that they do know the reasons such choice was made.

                1 Reply Last reply
                0
                • P pherschel

                  With C# getting to version 7+ I wish I could have some basic improvments. Is it me or do you get confused by this? Why can't I say

                  const DateTime today = DateTime.Now;

                  I can see readonly for parameters and such, but I would be happy using const there too

                  void Doit(const MyObj arg) ...

                  For properties, why can't I hide the worker variable for the rest of the class?

                  public int PageNbr
                  {
                  int _worker = 9;

                  get { return _worker;}
                  set { _worker = value; }
                  }

                  For destructors, why can't you give me option to destroy right away? I hate disposing with all its using code bloat. How about a free keyword on a variable or something to get me out of the business of resource management. If you open a file and a DB you have to nest usings before you even get started doing some work! Or maybe I'm missing something?

                  - Pete

                  I Offline
                  I Offline
                  irneb
                  wrote on last edited by
                  #30

                  From your example (i.e. placing DateTime.Now into a const) ... are you intending to save the compile timestamp into the executable code? What would be the intent? It would be pretty close (if not exactly the same) as the file date of the EXE/DLL. Only idea I could think for this to be used is to check if the executing file has been altered since it was compiled. Though for that I'd rather use some hash value instead, even just CRC would be more comprehensive than a timestamp. To me, the readonly idea makes more sense - i.e. it would save the time the program was started. This is the true difference between const and readonly: Const is as if the compiler changes your code to as if you typed in a literal value. Readonly is a way to make sure a variable only gets assigned its value once - from any calculation at runtime. The two ideas are not interchangeable, at least not in most cases.

                  1 Reply Last reply
                  0
                  • F Fabio Franco

                    TheGreatAndPowerfulOz wrote:

                    could

                    Yes, could, but should it? They behave differently and actually generate different IL code. The compiler could also determine that automatically, when generating IL code. But I don't think that's a good idea, but we will end up in another filosophical discussion. In my opinion this compiler behavior could generate those situations developers don't understand what's happening and why their code does not work as expected.

                    To alcohol! The cause of, and solution to, all of life's problems - Homer Simpson ---- Our heads are round so our thoughts can change direction - Francis Picabia

                    T Offline
                    T Offline
                    TheGreatAndPowerfulOz
                    wrote on last edited by
                    #31

                    Fabio Franco wrote:

                    should it?

                    Yes.

                    Fabio Franco wrote:

                    my opinion

                    Doubt it.

                    Fabio Franco wrote:

                    another filosophical discussion

                    That's what life is about.

                    #SupportHeForShe Government can give you nothing but what it takes from somebody else. A government big enough to give you everything you want is big enough to take everything you've got, including your freedom.-Ezra Taft Benson You must accept 1 of 2 basic premises: Either we are alone in the universe or we are not alone. Either way, the implications are staggering!-Wernher von Braun

                    1 Reply Last reply
                    0
                    • P pherschel

                      With C# getting to version 7+ I wish I could have some basic improvments. Is it me or do you get confused by this? Why can't I say

                      const DateTime today = DateTime.Now;

                      I can see readonly for parameters and such, but I would be happy using const there too

                      void Doit(const MyObj arg) ...

                      For properties, why can't I hide the worker variable for the rest of the class?

                      public int PageNbr
                      {
                      int _worker = 9;

                      get { return _worker;}
                      set { _worker = value; }
                      }

                      For destructors, why can't you give me option to destroy right away? I hate disposing with all its using code bloat. How about a free keyword on a variable or something to get me out of the business of resource management. If you open a file and a DB you have to nest usings before you even get started doing some work! Or maybe I'm missing something?

                      - Pete

                      I Offline
                      I Offline
                      irneb
                      wrote on last edited by
                      #32

                      These are probably a good idea. Though semantic-wise I'd actually want it to be a readonly instead. In C++ const is used to give the compiler a hint so it knows this parameter wont get changed inside the function, so a copy of it need not go onto its stack (just a direct memory link instead). But even there I'd actually want to rename it to something like readonly instead, const just confuses the hell out of me (is it only a param calculated at compile time, why then not just a normal const in the first place and do away with the param entirely?)

                      1 Reply Last reply
                      0
                      • P pherschel

                        With C# getting to version 7+ I wish I could have some basic improvments. Is it me or do you get confused by this? Why can't I say

                        const DateTime today = DateTime.Now;

                        I can see readonly for parameters and such, but I would be happy using const there too

                        void Doit(const MyObj arg) ...

                        For properties, why can't I hide the worker variable for the rest of the class?

                        public int PageNbr
                        {
                        int _worker = 9;

                        get { return _worker;}
                        set { _worker = value; }
                        }

                        For destructors, why can't you give me option to destroy right away? I hate disposing with all its using code bloat. How about a free keyword on a variable or something to get me out of the business of resource management. If you open a file and a DB you have to nest usings before you even get started doing some work! Or maybe I'm missing something?

                        - Pete

                        I Offline
                        I Offline
                        irneb
                        wrote on last edited by
                        #33

                        Here you may be onto something. I'd imagine getting something like this working at present would be a form of making automatic struct types for each property - incorporating their default properties with a get set as defined in code. Perhaps including it into the spec could be done more efficiently. It should definitely not be enforced though. In some cases the rest of the class does need to see the underlying data without needing to go through the accessor methods. Which in turn brings up another issue in C# ... the inability to have default properties like you can in VB-Net. In C# your closest match is to make implicit type-casting operator overloads.

                        1 Reply Last reply
                        0
                        • C Chris Maunder

                          pherschel wrote:

                          I can see readonly for parameters and such,

                          That's actually one of the things I love about Swift[^]: params are by default readonly

                          cheers Chris Maunder

                          I Offline
                          I Offline
                          irneb
                          wrote on last edited by
                          #34

                          That's actually a VERY good idea. I.e. if you wanted some by-value parameter and be able to change its value inside the function, decorate it with something like volatile (or perhaps duplicated would be a more apt naming). Or to just make it sound similar to out and ref ... call it something like in, or dup, or copy, ... This would allow for so much better optimisations across the board, while disallowing stupid things like assigning new values to by-value parameters unless you specifically state that's what you want.

                          1 Reply Last reply
                          0
                          • F Fabio Franco

                            TheGreatAndPowerfulOz wrote:

                            could

                            Yes, could, but should it? They behave differently and actually generate different IL code. The compiler could also determine that automatically, when generating IL code. But I don't think that's a good idea, but we will end up in another filosophical discussion. In my opinion this compiler behavior could generate those situations developers don't understand what's happening and why their code does not work as expected.

                            To alcohol! The cause of, and solution to, all of life's problems - Homer Simpson ---- Our heads are round so our thoughts can change direction - Francis Picabia

                            U Offline
                            U Offline
                            User 10247229
                            wrote on last edited by
                            #35

                            I'd like to see the let keyword added to C# for this purpose, to replace var in cases where the binding will not be mutated. Swift makes this distinction, and F# uses let with this meaning (and let mutable for what C# calls var). It also just feels really good, like a benevolent ruler. The variable wants to have this value, and you just have to let it. const is what JavaScript uses for non-mutable bindings, but in C# it already specifically refers to compile-time constants in C#. Also, const just doesn't feel as good as letting a variable follow its heart.

                            F 1 Reply Last reply
                            0
                            • U User 10247229

                              I'd like to see the let keyword added to C# for this purpose, to replace var in cases where the binding will not be mutated. Swift makes this distinction, and F# uses let with this meaning (and let mutable for what C# calls var). It also just feels really good, like a benevolent ruler. The variable wants to have this value, and you just have to let it. const is what JavaScript uses for non-mutable bindings, but in C# it already specifically refers to compile-time constants in C#. Also, const just doesn't feel as good as letting a variable follow its heart.

                              F Offline
                              F Offline
                              Fabio Franco
                              wrote on last edited by
                              #36

                              Member 10277807 wrote:

                              I'd like to see the let keyword added to C#

                              That would actually be pretty cool. It already exists with Linq, but it would a nice addition to the language itself, at least for value types. let has a functional nature and C# has been slowly adopting some of the functional programming features. I can see it come to C# soon enough.

                              To alcohol! The cause of, and solution to, all of life's problems - Homer Simpson ---- Our heads are round so our thoughts can change direction - Francis Picabia

                              1 Reply Last reply
                              0
                              • F Foothill

                                That's not entirely true. Look at streams. One would think this is correct.

                                using (var outerStream = new MemoryStream(someData))
                                {
                                using (var innerStream = new TextReader(outerStream))
                                {
                                // do something
                                }
                                }

                                However, the proper way is this

                                var outerStream = new MemoryStream(someData);
                                using (var innerStream = new TextReader(outerStream)
                                {
                                // do something
                                }

                                This is because the a stream will dispose of the underlying streams when you call Stream.Dispose(). I had code analysis bark at me all the time until I figured this one out.

                                if (Object.DividedByZero == true) { Universe.Implode(); } Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016

                                C Offline
                                C Offline
                                carloscs
                                wrote on last edited by
                                #37

                                Foothill wrote:

                                However, the proper way is this

                                var outerStream = new MemoryStream(someData);
                                using (var innerStream = new TextReader(outerStream)
                                {
                                // do something
                                }

                                This is because the a stream will dispose of the underlying streams when you call Stream.Dispose(). I had code analysis bark at me all the time until I figured this one out.

                                No, it's not: if "new TextReader(outerStream)" throws you get an unclosed outerStream. The code analysis tool you're using is broken unless it can prove that the TextReader constructor never throws, and I really doubt that 1) it's doing that analisys and 2) that it's good practice to encourage not doing the outer using because in one specific case it's guaranteed the inner stream constructor doesn't throw.

                                F 1 Reply Last reply
                                0
                                • C carloscs

                                  Foothill wrote:

                                  However, the proper way is this

                                  var outerStream = new MemoryStream(someData);
                                  using (var innerStream = new TextReader(outerStream)
                                  {
                                  // do something
                                  }

                                  This is because the a stream will dispose of the underlying streams when you call Stream.Dispose(). I had code analysis bark at me all the time until I figured this one out.

                                  No, it's not: if "new TextReader(outerStream)" throws you get an unclosed outerStream. The code analysis tool you're using is broken unless it can prove that the TextReader constructor never throws, and I really doubt that 1) it's doing that analisys and 2) that it's good practice to encourage not doing the outer using because in one specific case it's guaranteed the inner stream constructor doesn't throw.

                                  F Offline
                                  F Offline
                                  Foothill
                                  wrote on last edited by
                                  #38

                                  Sorry, TextReader was a bad example. I typed it for speed. I went into more detail here in this response[^]. There are, however, certain stream classes that implement IDisposable in which the disposal pattern goes further and disposes of underlying streams. The code analysis is built into VS 2015 and I run it with all the rules turned on so I don't think it's broken. It was detecting MS .Net code that was going to behave contrary to preconceptions of IDisposable objects.

                                  if (Object.DividedByZero == true) { Universe.Implode(); } Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016

                                  C 1 Reply Last reply
                                  0
                                  • F Foothill

                                    Sorry, TextReader was a bad example. I typed it for speed. I went into more detail here in this response[^]. There are, however, certain stream classes that implement IDisposable in which the disposal pattern goes further and disposes of underlying streams. The code analysis is built into VS 2015 and I run it with all the rules turned on so I don't think it's broken. It was detecting MS .Net code that was going to behave contrary to preconceptions of IDisposable objects.

                                    if (Object.DividedByZero == true) { Universe.Implode(); } Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016

                                    C Offline
                                    C Offline
                                    carloscs
                                    wrote on last edited by
                                    #39

                                    I agree that TextReader is a bad example for other reasons: probably the constructor doesn't throw. And I saw your other response, and it doesn't change things: it clarifies that Dispose does indeed call close on the outerStream, but it doesn't say a thing about what happens if the constructor throws. My point is that with:

                                    var outerStream = new AStream();
                                    using (var innerStream = new AnotherStream(outerStream)) {...}

                                    If "new AnotherStream(outerStream)" throws you get to hold the undisposed baby as innerStream.Dispose() never gets called and the outerStream isn't closed. Can't comment on VS code analysis, as I only use Resharper, but just for fun, trying VS2015 code analysis (all rules on) on this code:

                                    {
                                    var outerStream = new MyStream(); // line 59
                                    using (var innerStream = new MyStream(outerStream)) {
                                    bool i = innerStream.CanRead;
                                    Console.WriteLine("Read: " + i);
                                    }
                                    }
                                    {
                                    using (var outerStream = new MyStream())
                                    {
                                    using (var innerStream = new MyStream(outerStream))
                                    {
                                    bool i = innerStream.CanRead;
                                    Console.WriteLine("Read: " + i);
                                    }
                                    } // Line 73
                                    }

                                    [Line 59] Warning CA2000 In method 'Program.Main(string[])', call System.IDisposable.Dispose on object 'outerStream' before all references to it are out of scope. [Line 73] Warning CA2202 Object 'outerStream' can be disposed more than once in method 'Program.Main(string[])'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object. Bit schizophrenic: in the first case complains that Dispose isn't called and on the second complains that it's called twice :) Edit: - Moved Line 73 one line up... - VS code analysis agrees with me: the first warning says that with only one using it's not guaranteed that the outer stream gets closed. -(Re)thinking on this, VS code analysis is not smart enough: it knows that innerStream.Dispose() calls outerStream.Dispose() but doesn't know that Stream.Dispose() is (should always be) idempotent and can get called any number of times.

                                    F M 2 Replies Last reply
                                    0
                                    • C carloscs

                                      I agree that TextReader is a bad example for other reasons: probably the constructor doesn't throw. And I saw your other response, and it doesn't change things: it clarifies that Dispose does indeed call close on the outerStream, but it doesn't say a thing about what happens if the constructor throws. My point is that with:

                                      var outerStream = new AStream();
                                      using (var innerStream = new AnotherStream(outerStream)) {...}

                                      If "new AnotherStream(outerStream)" throws you get to hold the undisposed baby as innerStream.Dispose() never gets called and the outerStream isn't closed. Can't comment on VS code analysis, as I only use Resharper, but just for fun, trying VS2015 code analysis (all rules on) on this code:

                                      {
                                      var outerStream = new MyStream(); // line 59
                                      using (var innerStream = new MyStream(outerStream)) {
                                      bool i = innerStream.CanRead;
                                      Console.WriteLine("Read: " + i);
                                      }
                                      }
                                      {
                                      using (var outerStream = new MyStream())
                                      {
                                      using (var innerStream = new MyStream(outerStream))
                                      {
                                      bool i = innerStream.CanRead;
                                      Console.WriteLine("Read: " + i);
                                      }
                                      } // Line 73
                                      }

                                      [Line 59] Warning CA2000 In method 'Program.Main(string[])', call System.IDisposable.Dispose on object 'outerStream' before all references to it are out of scope. [Line 73] Warning CA2202 Object 'outerStream' can be disposed more than once in method 'Program.Main(string[])'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object. Bit schizophrenic: in the first case complains that Dispose isn't called and on the second complains that it's called twice :) Edit: - Moved Line 73 one line up... - VS code analysis agrees with me: the first warning says that with only one using it's not guaranteed that the outer stream gets closed. -(Re)thinking on this, VS code analysis is not smart enough: it knows that innerStream.Dispose() calls outerStream.Dispose() but doesn't know that Stream.Dispose() is (should always be) idempotent and can get called any number of times.

                                      F Offline
                                      F Offline
                                      Foothill
                                      wrote on last edited by
                                      #40

                                      I can see your point but accounting for errors in Stream constructors, which you can catch, isn't close to the point I was trying to make. I was just implying that it's generally a good idea to use using but is not always a straight forward case such as when using streams that one would assume derive from System.IO.Stream based on code symantics. What sets StreamReader and StreamWriter apart is that, while they behave exactly like a stream, they do not derive from System.IO.Stream. They derive from System.TextReader which in turn derives directly from System.MarshalByRefObject. So here we have two classes mimicking the behavior of an entire branch of classes but have subtle differences in implementation. Since most people would assume that StreamReader derived from IO.Stream, it can cause confusion when CA2202 messages warn that you're disposing of a object twice even thought you are actually following recommended best practices.

                                      if (Object.DividedByZero == true) { Universe.Implode(); } Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016

                                      C 1 Reply Last reply
                                      0
                                      • C carloscs

                                        I agree that TextReader is a bad example for other reasons: probably the constructor doesn't throw. And I saw your other response, and it doesn't change things: it clarifies that Dispose does indeed call close on the outerStream, but it doesn't say a thing about what happens if the constructor throws. My point is that with:

                                        var outerStream = new AStream();
                                        using (var innerStream = new AnotherStream(outerStream)) {...}

                                        If "new AnotherStream(outerStream)" throws you get to hold the undisposed baby as innerStream.Dispose() never gets called and the outerStream isn't closed. Can't comment on VS code analysis, as I only use Resharper, but just for fun, trying VS2015 code analysis (all rules on) on this code:

                                        {
                                        var outerStream = new MyStream(); // line 59
                                        using (var innerStream = new MyStream(outerStream)) {
                                        bool i = innerStream.CanRead;
                                        Console.WriteLine("Read: " + i);
                                        }
                                        }
                                        {
                                        using (var outerStream = new MyStream())
                                        {
                                        using (var innerStream = new MyStream(outerStream))
                                        {
                                        bool i = innerStream.CanRead;
                                        Console.WriteLine("Read: " + i);
                                        }
                                        } // Line 73
                                        }

                                        [Line 59] Warning CA2000 In method 'Program.Main(string[])', call System.IDisposable.Dispose on object 'outerStream' before all references to it are out of scope. [Line 73] Warning CA2202 Object 'outerStream' can be disposed more than once in method 'Program.Main(string[])'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object. Bit schizophrenic: in the first case complains that Dispose isn't called and on the second complains that it's called twice :) Edit: - Moved Line 73 one line up... - VS code analysis agrees with me: the first warning says that with only one using it's not guaranteed that the outer stream gets closed. -(Re)thinking on this, VS code analysis is not smart enough: it knows that innerStream.Dispose() calls outerStream.Dispose() but doesn't know that Stream.Dispose() is (should always be) idempotent and can get called any number of times.

                                        M Offline
                                        M Offline
                                        Mike Marynowski
                                        wrote on last edited by
                                        #41

                                        That's really really odd - is there any example of a Dispose() method throwing an ObjectDisposedException if called twice? Because if so, that's going against the guidelines for implementing a Dispose method. Dispose methods should never throw for exactly this reason (among others), so the Line 73 warning seems rather odd.

                                        Blog: [Code Index] By Mike Marynowski | Business: Singulink

                                        1 Reply Last reply
                                        0
                                        • F Foothill

                                          I can see your point but accounting for errors in Stream constructors, which you can catch, isn't close to the point I was trying to make. I was just implying that it's generally a good idea to use using but is not always a straight forward case such as when using streams that one would assume derive from System.IO.Stream based on code symantics. What sets StreamReader and StreamWriter apart is that, while they behave exactly like a stream, they do not derive from System.IO.Stream. They derive from System.TextReader which in turn derives directly from System.MarshalByRefObject. So here we have two classes mimicking the behavior of an entire branch of classes but have subtle differences in implementation. Since most people would assume that StreamReader derived from IO.Stream, it can cause confusion when CA2202 messages warn that you're disposing of a object twice even thought you are actually following recommended best practices.

                                          if (Object.DividedByZero == true) { Universe.Implode(); } Meus ratio ex fortis machina. Simplicitatis de formae ac munus. -Foothill, 2016

                                          C Offline
                                          C Offline
                                          carloscs
                                          wrote on last edited by
                                          #42

                                          Hmm... using the example on your other post (changed just enough to make the code compile):

                                          byte[] data = new byte[1000];
                                          MemoryStream memStream = new MemoryStream(data);
                                          CryptoStream decStream = new CryptoStream(memStream, SHA1.Create(), CryptoStreamMode.Read);

                                          using (StreamReader reader = new StreamReader(decStream)) {
                                          byte[] decryptedValue = Encoding.UTF8.GetBytes(reader.ReadToEnd());
                                          Console.WriteLine(decryptedValue);
                                          }

                                          We get a (for me accurate) CA2000 warning: object 'memStream' is not disposed along all exception paths. And... what's this, no warning about an undiposed decStream??? Let me check:

                                          SHA1 sha1 = SHA1.Create();
                                          byte[] data = new byte[1000];
                                          using (MemoryStream memStream = new MemoryStream(data)) {
                                          CryptoStream decStream = new CryptoStream(memStream, sha1, CryptoStreamMode.Read);

                                          StreamReader reader = new StreamReader(decStream);
                                          byte\[\] decryptedValue = Encoding.UTF8.GetBytes(reader.ReadToEnd());
                                          Console.WriteLine(decryptedValue);
                                          

                                          }

                                          No undisposed warnings with VS about this. Was going to say I could agree with this code, but no, even though it passes VS code analysis I disagree even more with it. Who can say what goes on in all the undisposed inner streams in the case of an exception? For me: a) (repeat from my other post) It seems that while VS2015 (and 2017, just tested) code analysis is smart enough to know that innerStream.Dispose calls outerSteam.Dispose it's not smart enough to know that Dispose in streams should be idempotent and should be able to be called several times. b) It's really strange there being a best practice that doesn't ensure every stream is closed in all code paths, including exceptions.

                                          F 1 Reply Last reply
                                          0
                                          Reply
                                          • Reply as topic
                                          Log in to reply
                                          • Oldest to Newest
                                          • Newest to Oldest
                                          • Most Votes


                                          • Login

                                          • Don't have an account? Register

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • World
                                          • Users
                                          • Groups