9

I have the following sealed class. I'm trying to return the list as a ReadOnlyCollection. Tried a couple of things but I'm not getting the hang of this. So how do I return or cast the list to the readonly collection?

    public sealed class UserValues
    {
        private readonly List<UserValue> _Values = new List<UserValue>();

        public ReadOnlyCollection<UserValues> Values
        {
            get
            {
                return _Values;
            }
        }
    }
DMKE
  • 4,553
  • 1
  • 31
  • 50
Debbie
  • 125
  • 1
  • 1
  • 6
  • The class `List` has `object` as its base class. The `ReadOnlyCollection` class is therefore not a base class, and since no implicit operators are defined, you cannot just convert between these two classes like that. If you want to create a new `ReadOnlyCollection<>`, use `_Values.AsReadOnly()`. If you use the newest .NET version (4.5, VS2012), you can change the property type to the interface `IReadOnlyList`. Since `List<>` implements `IReadOnlyList<>`, this requires no cast. It is not as secure (if you don't trust the consumers of your class). – Jeppe Stig Nielsen May 05 '13 at 20:43
  • A determined consumer could use reflection to get at the `IList` wrapped by a `ReadOnlyCollection` too, though -- there's not much you can do to stop somebody messing with the innards of your classes if they really, really want to. I figure if they're *that* determined to break things, they'll find a way, so it's not worth worrying too much about. – Jeremy Todd May 05 '13 at 20:52
  • I tried doing _Values.AsReadOnly() as Jeppe mentioned and get the exact same compiler error. And I'm only using vs2008, so I don't have access to the interface he mentioned. – Debbie May 12 '13 at 15:29

3 Answers3

4

Try:

return new ReadOnlyCollection<UserValue>(_Values);

Edit:

Given what you've said to Jon, your code doesn't make sense. Your get is referencing a type of List<UserValue>, but you're wanting to convert it to a type of ReadOnlyCollection<UserValues>, which cant be done - that's 2 collections of 2 different types.

We'll need more information to help you answer this question. Are you wanting your UserValues class to return a collection of UserValues types, or a collection of UserValue types? Your code implies UserValue, but your follow on comments state UserValues. Are you sure your supervisor didn't make a typo?

If not, you'll need some internal collection like so:

private readonly List<UserValues> _MoreValues = new List<UserValues>();

And then return that, in the syntax that I (or others who have answered - all the answers given are valid for converting a List to a ReadOnlyCollection) have shown.

Note that my code compiles targeting .Net 3.5, presuming that the types are compatible (meaning ReadOnlyCollection<UserValue> wraps List<UserValue>, or both are UserValues).

Gjeltema
  • 4,122
  • 2
  • 24
  • 31
  • That will create a new instance of the `ReadOnlyCollection` wrapper every time the property is accessed -- probably not ideal. – Jeremy Todd May 05 '13 at 20:32
  • It's true that it's not "ideal", but in this case, it answers the question and gets the questioner on their way. You could do .AsReadOnly as the others have suggested, but if you decompile it you see that it does this: return new ReadOnlyCollection((IList) this);. The "best" solution (depending on the person's needs) would be to create a member that holds the readonly collection, and return that... but that seemed beyond the scope of the question, which was a casting problem. – Gjeltema May 05 '13 at 20:37
  • I just had time to try the code above and it will not compile either. – Debbie May 12 '13 at 15:15
  • No, my code does compile (it compiles in my VS2012 on my machine right now), but it doesn't compile when you change it to be of type ``, which is what you're saying you need in your comment to Jon. I'll add more thoughts in my answer... – Gjeltema May 12 '13 at 15:27
4

You're getting the compile-time error because a List<UserValue> is not a ReadOnlyCollection<UserValue>, nor is it implicitly convertible to that. (I assume you meant ReadOnlyCollection<UserValue> instead of ReadOnlyCollection<UserValues> by the way?)

It's probably simplest to use List<T>.AsReadOnly - but you might as well only create it once:

public sealed class UserValues
{
    private readonly List<UserValue> values = new List<UserValue>();
    private readonly ReadOnlyCollection<UserValue> valuesView;

    public UserValues()
    {
        valuesView = values.AsReadOnly();
    }

    public ReadOnlyCollection<UserValues> Values { get { return valuesView; } }
}

The ReadOnlyCollection<T> really is just a view - so changes to the underlying collection will be visible through the view.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Hi Jon. No, I really did mean "UserValues" with an s. My supervisor gave me the code stubs and I'm supposed to fill them in without changing the method signature. This would be a whole lot easier if I could change the signature to match the variable. – Debbie May 12 '13 at 15:21
  • @Debbie: Well the code that you've written in the question makes very little sense. Are you sure it's not a typo? I suggest you ask your supervisor. It makes sense for a `UserValues` class to have a collection of `UserValue` objects, but not for it to have a collection of `UserValues` objects. – Jon Skeet May 12 '13 at 15:57
2

_Values is a List<UserValue>, not a ReadOnlyCollection<UserValue> (they're not related, as far as the compiler knows), so you can't return _Values directly. You can either create a ReadOnlyCollection<T> from your list and return that, like:

private List<UserValue> _Values = [whatever];
private ReadOnlyCollection<UserValue> _ValuesWrapper;

public UserValues()
{
  _ValuesWrapper = _Values.AsReadOnly();
}

public ReadOnlyCollection<UserValue> Values
{
  get { return _ValuesWrapper; }
}

...or, if you're just looking for a read-only way of accessing your collection and don't need a ReadOnlyCollection<UserValue> object specifically, you could change your property to return a read-only interface that List<T> implements, and your application could use that instead. .NET 4.5 introduced some read-only collection interfaces that are great for this kind of thing:

public IReadOnlyList<UserValue> Values
{
  get { return _Values; }
}
Jeremy Todd
  • 3,261
  • 1
  • 18
  • 17
  • Sadly, I do not have access to .net 4.5. My Visual studio is 2008. So far, nothing mentioned in here will work with the version of Visual Studios I have. Can anyone kind of "dumb down" their replies from .net 4.5 to .net 3.5, please? – Debbie May 12 '13 at 15:32
  • My code compiles targeting .Net 3.5. Additionally, the .AsReadOnly() that Jeremy and Jon have recommended has been supported since .Net 2.0, so theirs will compile too (http://msdn.microsoft.com/en-us/library/e78dcd75(v=vs.90).aspx). The issue is with your need for `UserValues` readonly collection wrapping a `UserValue` collection class. Please provide more information as I noted in my answer to help us answer your question. – Gjeltema May 12 '13 at 15:50