3
public Item getItem(ulong itemId)
{
    Item item = items[itemId]
    return item;
}

Now the problem is: the callee of getItem must be able to retrieve information that item holds; but not modify it.

Sorry, but I know C++ better than C#... I'm afraid I must fall back to C++ for a teeny tiny while just to make myself clear what I'm trying to achieve... So this is what I'd do in C++ in this situation:

const Item& getItem(unsigned long itemId)
{
    const Item& item = items[itemId];
    return item;
}

Very well - now whoever called getItem may call getters on item, but not setters.

I need to achieve a similar result in C#... is it possible?

  • It depends if `Item` is a `class` or a `struct`. – John Alexiou May 24 '18 at 17:25
  • While you might be able to hack something together, C# isn't usually used that way. Is there anything stopping you changing `Item` to make the properties read only? – DavidG May 24 '18 at 17:25
  • 2
    If you need that, you should probably use a new object without the setters, like `ReadOnlyItem` that wraps the `Item` without the setters. – Ron Beyer May 24 '18 at 17:27
  • 3
    @RonBeyer or return an interface that Item implements, that only exposes getters – Dave M May 24 '18 at 17:34
  • 3
    For completeness (just in case it's not already clear): C# has no notion of C++'s "const correctness" (nor do most other languages, for that matter). There's some support for immutability with structs, but since immutability is not the *purpose* of structs (and there are other consequences of using them) that's not a usual approach. If your code base has a real need to block callers from doing a bad thing, a wrapper interface or class is the way to go, as others have mentioned, but in many cases where C++ would use `const`, C# code bases would just go "don't do that, then". – Jeroen Mostert May 24 '18 at 17:42
  • Can you please give a simplified example of `Item`. – John Alexiou May 24 '18 at 17:51
  • @ja72 Chess. `Item` is the current state of the board. The idea is that a player whose turn it currently is may make a move (modifying the state). The other player, and also possible spectators, must be able to see the board; but not make moves. –  May 24 '18 at 17:58
  • Are the players and the spectators just other objects that you too wrote the code for, or are they third-party, living in possibly hostile libraries? Only the latter case is something that would warrant protecting against. It's a good example of where C++ would use `const` as a matter of course, and in C# a read-only wrapper would usually be unnecessary work. (If the code was possibly *actively hostile*, as in a security risk, even in C++ this would not suffice, as you can `const_cast` the protection away. You would actually have to copy data.) – Jeroen Mostert May 24 '18 at 18:01
  • @JeroenMostert I am aware that `const` is a no security measure. My concerns are here purely aesthetics; I'm returing the board state to my own code that will serialize it to JSON and send that JSON to clients. I find it not very "clean" if this code will be capable of issuing move commands. Though, if this is not how things are done in C# and if C# developers would simply say "then don't call the `makeMove` method in the networking code then OK, I accept that and thank you for enlightening me. :) –  May 24 '18 at 18:08
  • Yes, for pure aesthetics C# programmers would not usually break out anything to guarantee immutability -- anyone modifying the board in serialization code could not possibly be doing so by mistake, so it's not a mistake worth guarding against. That's not to say immutability in C# isn't a very interesting topic that [luminaries have written about](https://blogs.msdn.microsoft.com/ericlippert/2007/11/13/immutability-in-c-part-one-kinds-of-immutability/), though, and you could have a completely immutable chess board as well, which would preclude the mistake *and* do nice things for threading. :-) – Jeroen Mostert May 24 '18 at 18:12

1 Answers1

2

You can use interfaces to surface only getters while still supporting setters in the class that implements the interface. As long as you return the interface, any consumer will be restricted to only the getters that interface defines.

public interface IItem {
    string SomeProperty { get; }
}

public class Item : IItem {
    public string SomeProperty { get; set; }
}

public class ItemHandler {
    private Item _item = new Item();

    public IItem getItem() {
        _item.SomeProperty = "A Value";
        return _item;
    }
}

class Program {
    static void Main(string[] args) {
        var itemHandler = new ItemHandler();
        var item = itemHandler.getItem();

        // You can read the property
        Console.WriteLine(item.SomeProperty);

        // You can't write to the property
        //item.SomeProperty = "A New Value";
    }
}
Marc LaFleur
  • 31,987
  • 4
  • 37
  • 63
  • Wow - VERY COOL!!!! – John Bustos May 24 '18 at 18:00
  • 1
    Of course, the client can always cast into the original object and access the setter. `( item as Item).SomeProperty = "Cheating";` is perfectly legal and won't throw an error. I think the _OP_ needs enforced immutability by design. – John Alexiou May 24 '18 at 18:06
  • Generally, you only do something like this to protect devs from _accidently_ stepping on data. To make it truly immutable things get a lot more complex. The comes down to if you're trying to guide the developer or enforce constraints. – Marc LaFleur May 24 '18 at 19:14