Results 1 to 6 of 6

Thread: [RESOLVED] Mapping non-generic interfaces to generic collections

  1. #1

    Thread Starter
    Hyperactive Member
    Join Date
    Apr 2007
    Location
    cobwebbed to PC
    Posts
    311

    Resolved [RESOLVED] Mapping non-generic interfaces to generic collections

    In this SO question, https://stackoverflow.com/a/46363899/1292918, Hans Passant mentions:

    "Mapping your own non-generic interface to a generic collection type"

    What is meant by this exactly? How does one go about it?

    My initial though was that it meant replacing generic interface usage with non-generic ones, eg, replacing IList<T> usage with IEnumerable or ArrayList usage.

    But then I thought maybe it means creating my own IStringList type to replace IList<T> with instead of either of the aforementioned types... that would mean the following, right?

    c# Code:
    1. public interface IStringList : IList<string> { /* empty */ }

    Update:Asked Hans about this latter code in a comment, he said "No, encapsulation is the appropriate choice". Unsure what is meant by this but SO is not the format to dig into details really
    Last edited by wolf99; Sep 22nd, 2017 at 08:07 AM.
    Thanks

  2. #2
    PowerPoster techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,687

    Re: Mapping non-generic interfaces to generic collections

    Not sure... the way I interpreted the title is you create your own type on the surface, using generics under the covers. For instance, you can't expose a List<Integer> to COM ... so you create a new class structure that has all the features of a List<T> but is type specific externally, even though it is using generics under the cover. Then COM interacts directly with the non-generic class. That's what's meant by the comment "encapsulation is the appropriate choice." ... you encapsulate the functionality of the generic into something that isn't generic.

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  3. #3

    Thread Starter
    Hyperactive Member
    Join Date
    Apr 2007
    Location
    cobwebbed to PC
    Posts
    311

    Re: Mapping non-generic interfaces to generic collections

    Thanks TechGnome!

    My understanding of what is actually the best way to do this is... patchy (Friday brain). Something like the following should cover it?

    c# Code:
    1. public class StringList : IList<string>
    2. {
    3.     private List<string> items;
    4.  
    5.     public StringList() { items = new List<string>(); }
    6.     public StringList(System.Collections.IEnumerable collection) { items = new List<string>(collection as IEnumerable<string>); }
    7.     public StringList(int capacity) { items = new List<string>(capacity); }
    8.     public int Capacity { get=> items.Capacity; set => items.Capacity = value; }
    9.     public int Count { get => items.Count; }
    10.     public string this[int index] { get => items[index]; set => items[index] = value; }
    11.  
    12.     //etc
    13. }
    Last edited by wolf99; Sep 22nd, 2017 at 09:11 AM.
    Thanks

  4. #4
    PowerPoster techgnome's Avatar
    Join Date
    May 2002
    Posts
    34,687

    Re: Mapping non-generic interfaces to generic collections

    Yes... except that second constructor isn't necessary... you're not doing anything with the collection passed in, and I don't know if IEnumerable is exposed to COM, so I don't even know if COM could call that particular signature.

    Also, I'm assuming that the //etc includes all your .Add methods, .Remove, .RemoveAt, and so on...

    -tg
    * I don't respond to private (PM) requests for help. It's not conducive to the general learning of others.*
    * I also don't respond to friend requests. Save a few bits and don't bother. I'll just end up rejecting anyways.*
    * How to get EFFECTIVE help: The Hitchhiker's Guide to Getting Help at VBF - Removing eels from your hovercraft *
    * How to Use Parameters * Create Disconnected ADO Recordset Clones * Set your VB6 ActiveX Compatibility * Get rid of those pesky VB Line Numbers * I swear I saved my data, where'd it run off to??? *

  5. #5
    You don't want to know.
    Join Date
    Aug 2010
    Posts
    4,578

    Re: Mapping non-generic interfaces to generic collections

    I seem to always regret trying to help you.
    What an *******. I wouldn't bother asking him any further questions, he doesn't know anything worth knowing. It's a good thing he's deleted his answer, but I've flagged it anyway.

    What he meant before revealing himself as a jerk:

    Generic types cannot be exposed to COM. At all. There are no tricks to make it work. You can't move it to a property to make it work, because it's still a generic type.

    But you CAN write a type that contains the generic type and delegates work to the generic type. Non-generic types can be exposed. IEnumerable, in particular, is visible to COM, as is IList.

    So here's what I think he meant by "encapsulation is the way", he was probably in a hurry to play with the broomstick in his anus and didn't have time to type it out. It'd be nice if he'd actually answered your question. I'm no COM expert but I think I see the shape of this problem:

    Say you have an IList<string> you want to expose to COM. You can't. The end. But IList is COM-visible. It's not enough to just cast your object to an IList, though. It's still an IList<string> and is still going to cause problems. So you need to make something that HAS AN IList<string>, not something that IS AN IList<string>:
    Code:
    [ComVisible(true)]
    public class StringListWrapper : IList {
    
        private IList _internalList;
    
        public StringListWrapper(IList genericList) {
            if (!(genericList is IList<string>)) {
                throw new ArgumentException("Sorry, Charlie.", "genericList");
            }
    
            _internalList = genericList;
        }
    
        public int Add(object value) {
            return _internalList.Add(value);
        }
    
        public void Clear() {
            _internalList.Clear();
        }
    
        // ... dozens of other methods
    I'm almost positive if you follow that pattern it will work, or it's at least close to what Hans was getting at before nature called. The important part about writing these wrappers is if you expose a generic type AT ALL, even if you hide it behind a cast to a less-specific interface, that's not going to work in COM. But my guess is if you make a type like this that keeps all the generic code on the ".NET side", it will work.

    But it's probably just easier to use ArrayList and have some type discipline.

    *edit* Oh, I see you sort of stumbled on that already. Whoops. I saw the SO post and got so mad I had to type a reply and didn't read the rest.
    This answer is wrong. You should be using TableAdapter and Dictionaries instead.

  6. #6

    Thread Starter
    Hyperactive Member
    Join Date
    Apr 2007
    Location
    cobwebbed to PC
    Posts
    311

    Re: Mapping non-generic interfaces to generic collections

    Thanks for the added clarity Sitten. I don't have enough rep to see deleted answers on SO, so I had no idea why he'd deleted it... I opened the question here in addition to the SO one because I didn't want to be a help vampire in SO's Q&A format (or at all!) but the line is much tighter there). I thought my single line comments wouldn't be anywhere near that line even so. Funny thing is I rarely get answers from Hans (since ever) so I'm not sure what his "always" refers to... never mind

    I had begun by considering ArrayList but it seemed to be kind of a code smell to cripple the types used in the interface so much for .Net clients just to try to serve COM clients (equal probability of both but there is no data on that yet). Chances are I'd end up constructing the collection initially as a tightly-typed List<> anyway before passing it out to the client as an ArrayList who, if a .Net client, would then likely want to convert it back again - code smell.

    Below is what I've got now. Hopefully it is generic enough and tidy enough to be useful to others also, at least saving them some typing. I may add this to the code vault if you folks think it would be useful in that regard?

    FYI, if either of you are on SO, and would like to copy-pasta your answers over to there, they are both good enough to answer the question as it and they stand so I'll accept there too :-)

    Usage:
    c# Code:
    1. IStringList Foo1 = new StringList();
    2.     IList Foo2 = new StringList();
    3.     // or either of
    4.     IList<string> Foo3 = new StringList();
    5.     StringList Foo4 = new StringList();
    6.     // though sadly it doesn't work the other way
    7.     IStringList Foo5 = new List<string>(); // doesn't work

    c# Code:
    1. public interface IStringList : IList<string>, IList
    2.     {
    3.         new int Count { get; }
    4.         new string this[int index] { get; set; }
    5.     }
    6.  
    7.     public class StringList : IStringList
    8.     {
    9.         private const string ConversionErrorMessageFormatString = "Cannot convert from {0} to {1}.";
    10.         private List<string> items;
    11.  
    12.         public int Capacity { get => items.Capacity; set => items.Capacity = value; }
    13.         public bool IsFixedSize { get => ((IList)items).IsFixedSize; }
    14.         public int Count { get => items.Count; }
    15.         public bool IsReadOnly { get => ((IList<string>)items).IsReadOnly; }
    16.         public bool IsSynchronized { get => ((ICollection)items).IsSynchronized; }
    17.         public string this[int index] { get => items[index]; set => items[index] = value; }
    18.         object IList.this[int index] { get => ((IList)items)[index]; set => ((IList)items)[index] = value; }
    19.         object ICollection.SyncRoot { get => ((ICollection)items).SyncRoot; }
    20.  
    21.         public StringList()
    22.         {
    23.             items = new List<string>();
    24.         }
    25.  
    26.         public StringList(int capacity)
    27.         {
    28.             items = new List<string>(capacity);
    29.         }
    30.  
    31.         public StringList(IList list)
    32.         {
    33.             if (!(list is IList<string>))
    34.             {
    35.                 throw new ArgumentException(
    36.                     String.Format(ConversionErrorMessageFormatString,
    37.                     list.GetType().ToString(),
    38.                     this.GetType().ToString()),
    39.                     nameof(list));
    40.             }
    41.             items = list as List<string>;
    42.         }
    43.  
    44.         public StringList(IEnumerable collection)
    45.         {
    46.             if (!(collection is IEnumerable<string>))
    47.             {
    48.                 throw new ArgumentException(
    49.                     String.Format(ConversionErrorMessageFormatString,
    50.                     collection.GetType().ToString(),
    51.                     this.GetType().ToString()),
    52.                     nameof(collection));
    53.             }
    54.             items = new List<string>(collection as IEnumerable<string>);
    55.         }
    56.  
    57.         public void Add(string item)
    58.         {
    59.             items.Add(item);
    60.         }
    61.  
    62.         int IList.Add(object item)
    63.         {
    64.             return ((IList)items).Add(item);
    65.         }
    66.  
    67.         public void AddRange(IEnumerable collection)
    68.         {
    69.             items.AddRange(collection as IEnumerable<string>);
    70.         }
    71.  
    72.         public int BinarySearch(int index, int count, string item, IComparer comparer)
    73.         {
    74.             return items.BinarySearch(index, count, item, (IComparer<string>)comparer);
    75.         }
    76.  
    77.         public int BinarySearch(string item)
    78.         {
    79.             return items.BinarySearch(item);
    80.         }
    81.  
    82.         public int BinarySearch(string item, IComparer comparer)
    83.         {
    84.             return items.BinarySearch(item, (IComparer<string>)comparer);
    85.         }
    86.  
    87.         public void Clear()
    88.         {
    89.             items.Clear();
    90.         }
    91.  
    92.         public bool Contains(string item)
    93.         {
    94.             return items.Contains(item);
    95.         }
    96.  
    97.         bool IList.Contains(object item)
    98.         {
    99.             return ((IList)items).Contains(item);
    100.         }
    101.  
    102.         public void CopyTo(string[] array)
    103.         {
    104.             items.CopyTo(array);
    105.         }
    106.  
    107.         public void CopyTo(string[] array, int arrayIndex)
    108.         {
    109.             items.CopyTo(array, arrayIndex);
    110.         }
    111.  
    112.         void ICollection.CopyTo(Array array, int arrayIndex)
    113.         {
    114.             ((ICollection)items).CopyTo(array, arrayIndex);
    115.         }
    116.  
    117.         public void CopyTo(int index, string[] array, int arrayIndex, int count)
    118.         {
    119.             items.CopyTo(index, array, arrayIndex, count);
    120.         }
    121.  
    122.         public IEnumerator GetEnumerator()
    123.         {
    124.             return items.GetEnumerator();
    125.         }
    126.        
    127.         IEnumerator<string> IEnumerable<string>.GetEnumerator()
    128.         {
    129.             return items.GetEnumerator();
    130.         }
    131.  
    132.         public StringList GetRange(int index, int count)
    133.         {
    134.             return new StringList(items.GetRange(index, count));
    135.         }
    136.  
    137.         public int IndexOf(string item)
    138.         {
    139.             return items.IndexOf(item);
    140.         }
    141.  
    142.         int IList.IndexOf(object item)
    143.         {
    144.             return ((IList)items).IndexOf(item);
    145.         }
    146.  
    147.         public int IndexOf(string item, int index)
    148.         {
    149.             return items.IndexOf(item, index);
    150.         }
    151.  
    152.         public int IndexOf(string item, int index, int count)
    153.         {
    154.             return items.IndexOf(item, index, count);
    155.         }
    156.  
    157.         public void Insert(int index, string item)
    158.         {
    159.             items.Insert(index, item);
    160.         }
    161.  
    162.         void IList.Insert(int index, object item)
    163.         {
    164.             ((IList)items).Insert(index, item);
    165.         }
    166.  
    167.         public void InsertRange(int index, IEnumerable collection)
    168.         {
    169.             items.InsertRange(index, (IEnumerable<string>)collection);
    170.         }
    171.  
    172.         public int LastIndexOf(string item)
    173.         {
    174.             return items.LastIndexOf(item);
    175.         }
    176.  
    177.         public int LastIndexOf(string item, int index)
    178.         {
    179.             return items.LastIndexOf(item, index);
    180.         }
    181.  
    182.         public int LastIndexOf(string item, int index, int count)
    183.         {
    184.             return items.LastIndexOf(item, index, count);
    185.         }
    186.  
    187.         public bool Remove(string item)
    188.         {
    189.             return items.Remove(item);
    190.         }
    191.  
    192.         void IList.Remove(object item)
    193.         {
    194.             ((IList)items).Remove(item);
    195.         }
    196.  
    197.         public void RemoveAt(int index)
    198.         {
    199.             items.RemoveAt(index);
    200.         }
    201.  
    202.         public void RemoveRange(int index, int count)
    203.         {
    204.             items.RemoveRange(index, count);
    205.         }
    206.  
    207.         public void Reverse()
    208.         {
    209.             items.Reverse();
    210.         }
    211.  
    212.         public void Reverse(int index, int count)
    213.         {
    214.             items.Reverse(index, count);
    215.         }
    216.  
    217.         public void Sort()
    218.         {
    219.             items.Sort();
    220.         }
    221.  
    222.         public string[] ToArray()
    223.         {
    224.             return items.ToArray();
    225.         }
    226.  
    227.         public void TrimExcess()
    228.         {
    229.             items.TrimExcess();
    230.         }
    231.     }
    Last edited by wolf99; Sep 25th, 2017 at 05:15 AM.
    Thanks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width