Coffeehouse Thread

24 posts

Forum Read Only

This forum has been made read only by the site admins. No new threads or comments can be added.

const ref in C#

Back to Forum: Coffeehouse
  • User profile image
    VBJB

    Can someone explain to me why you cannot pass an object/variable into a method as const ref in C#/.net or am I beating a dead horse?

    What is the most efficient way to pass in large objects into a method and be reassured it will not be changed in the method? I do not want to pass by value.

    I did some searching online and could not find and technical papers on why const ref is not a part of .net.

    Thanks for your input.

  • User profile image
    ironichi

    Declaring as 'static' won't help you?

  • User profile image
    MB

    const needs to be fully evaluated at compile time and needs to be a type that can be implicitly converted... hence the no-go on object.

    If you are simply trying to preserve the inital state of an object passed to you, perhaps you could create a new copy when you receive it ?

  • User profile image
    Randolpho

    No, there's no equivalent to a const ref in C#.

    That said, there's a fairly simple way to get around it: interfaces. Create an interface for your object that does not allow modifications of the object, and pass the object cast as that interface to the method, rather than the whole object itself. The calling method cannot then directly modify the original object.

    NOTE: With .NET reflection all bets are off. Even private methods are fair game with reflection.

    So it basically boils down to trust. Do you trust the method you're calling not to modify your object?

  • User profile image
    phreaks

    I don't understand.

    Why pass it as ref if you don't want it changed in the first place?

    Perplexed

  • User profile image
    cheong

    Or try to provide a function that use StackTrace to attempt to find the immediate caller, if it's empty or an "recognized" one then allow write, deny otherwise.

    Btw, it'd be too heavyduty to do this on every "set methods" and may take more time to execute then just have the object be passed by value.

    Recent Achievement unlocked: Code Avenger Tier 4/6: You see dead program. A lot!
    Last modified
  • User profile image
    cheong

    phreaks wrote:
    I don't understand.

    Why pass it as ref if you don't want it changed in the first place?


    Because it takes time and space to copy a large object. Imagine if the method will be called in tight loop... I bet the GC won't like it(quick allocation and free), or will it?

    Passing by ref don't create duplicates.

    Recent Achievement unlocked: Code Avenger Tier 4/6: You see dead program. A lot!
    Last modified
  • User profile image
    Sven Groot

    cheong wrote:
    
    phreaks wrote:
    I don't understand.

    Why pass it as ref if you don't want it changed in the first place?


    Because it takes time and space to copy a large object. Imagine if the method will be called in tight loop... I bet the GC won't like it(quick allocation and free), or will it?

    Passing by ref don't create duplicates.


    That only applies to value types (structs). In .Net, reference types (classes) are never copied. Adding the ref keyword for a reference type means you are passing the reference by reference.

    Even when you are using value types the GC doesn't come into play, since value types always live on the stack and are not garbage collected.

    The problem with allowing const parameters is that you must then also have const methods like in C++. Without this, the compiler wouldn't be able to guarantee that a property or method call wouldn't change the object. Over the years it has been discovered that there are some disadvantageous to having two different classes of code (const and non-const) where one can't call the other, which is probably why the CLR/C# teams didn't put this in.

    Randolpho wrote:
    That said, there's a fairly simple way to get around it: interfaces. Create an interface for your object that does not allow modifications of the object, and pass the object cast as that interface to the method, rather than the whole object itself. The calling method cannot then directly modify the original object.


    Using an interface that provides only read-only access wouldn't work, since the function can always cast it back to the original object. The only thing that really works is a read only wrapper, which is indeed what happens when you use e.g. List<T>.AsReadOnly().

  • User profile image
    cheong

    Sven Groot wrote:
    
    That only applies to value types (structs). In .Net, reference types (classes) are never copied. Adding the ref keyword for a reference type means you are passing the reference by reference.

    Oops. I've forgotten that detail. Tongue Out

    Sven Groot wrote:
    
    Using an interface that provides only read-only access wouldn't work, since the function can always cast it back to the original object. The only thing that really works is a read only wrapper, which is indeed what happens when you use e.g. List<T>.AsReadOnly().


    Learnt something new today. Big Smile

    Recent Achievement unlocked: Code Avenger Tier 4/6: You see dead program. A lot!
    Last modified
  • User profile image
    sich

    One more reason for not having "const" behavior on methods is that the CLS restricts the range of capabilities of the CLR to support a wide range of languages. In this case, not each of languages supports const methods and parameters, and they couldn't work with code written in language which supports and uses them. Btw, Managed C++ supports const methods and parameters, as it has done for many years before .Net, but ONLY with C++ code. Actually, C++ compiler adds custom attributes which indicate usage of const modifier, but only C++ compiler is aware of this attribute.

  • User profile image
    Yggdrasil

    Sven Groot wrote:
    Using an interface that provides only read-only access wouldn't work, since the function can always cast it back to the original object.


    I'm nearly C++-illiterate, but I remember it said that even in C++ you can cast away the const into a non-const reference, making it pretty much equivalent to passing an interface: it's there to prevent accidental mistakes, not neutralize code that actively tries to corrupt data. For untrusted code, cloning your object is the best option.

  • User profile image
    Sven Groot

    Yggdrasil wrote:
    
    Sven Groot wrote:
    Using an interface that provides only read-only access wouldn't work, since the function can always cast it back to the original object.


    I'm nearly C++-illiterate, but I remember it said that even in C++ you can cast away the const into a non-const reference.

    Yes it can, but the results of trying to write to an object whose const-ness was casted away is undefined by the standard, so it's up to the compiler to decide what happens. It's therefore not a very safe thing to do.

  • User profile image
    stevo_

    cheong wrote:
    
    Sven Groot wrote:
    
    That only applies to value types (structs). In .Net, reference types (classes) are never copied. Adding the ref keyword for a reference type means you are passing the reference by reference.

    Oops. I've forgotten that detail.

    Sven Groot wrote:
    
    Using an interface that provides only read-only access wouldn't work, since the function can always cast it back to the original object. The only thing that really works is a read only wrapper, which is indeed what happens when you use e.g. List<T>.AsReadOnly().


    Learnt something new today.



    Talking of lists anyway, you should only use them as the working object, if you ever need to return a generic list of items, you should return a generic collection, the AsReadOnly method is most likely only calling something like:

    return new ReadOnlyCollection<T>(this);

    Same again if you are collating data inside a method to return, you shouldn't return the generic list directly, instead return a new generic collection by sending the list as the ctor arg.

  • User profile image
    Randolpho

    Sven Groot wrote:
    Using an interface that provides only read-only access wouldn't work, since the function can always cast it back to the original object. The only thing that really works is a read only wrapper, which is indeed what happens when you use e.g. List<T>.AsReadOnly().


    Excellent point!

    But remember, with reflection the wrapper class can essentially be unwrapped, as the calling method can simply access the original object through the wrapper's private storage.

    So, VBJB, the answer is this: You cannot get a const reference in C#, period, because the runtime lets you access pretty much anything. Also, as has already been mentioned, even with C++ most compilers have workarounds that essentially let you cast away constness.

    It boils down to trust. Do you trust the calling method not to mess with your object?

  • User profile image
    nlondon

    How about implementing ICloneable and then passing in a clone?

  • User profile image
    Randolpho

    nlondon wrote:
    

    How about implementing ICloneable and then passing in a clone?



    Explicitly forbidden by the original post -- the object is too large to be copied/cloned

  • User profile image
    VBJB

    Sven Groot wrote:
    

    That only applies to value types (structs). In .Net, reference types (classes) are never copied. Adding the ref keyword for a reference type means you are passing the reference by reference.



    So are you saying that when we are passing by value we are really passing the *address* of the object by value?

    If this is true, then passing big objects by value is not that bad, but I want to clarify this first.

    Someone?

  • User profile image
    Minh

    VBJB wrote:
    

    So are you saying that when we are passing by value we are really passing the *address* of the object by value?

    If this is true, then passing big objects by value is not that bad, but I want to clarify this first.

    Someone?


    Yes.

    SomeReallyHugeClass obj = new SomeReallyHugeClass();

    DoSomeWork(obj);

    //-----

    void DoSomeWork(SomeReallyHugeClass p)
    {
       // p is passed "by value" as C-style pointer

       p.SomeProperty = "something else";  // perfectly legal

       p = new SomeReallyHugeClass();  // won't compile
    }

    void DoSomeMoreWork(ref SomeReallyHugeClass p)
    {
       p = new SomeReallyHugeClass();  // Now, it's OK
    }

Conversation locked

This conversation has been locked by the site admins. No new comments can be made.