High Plains Grifter
High Plains Grifter

Reputation: 1571

What Type to use for Generic IList Method

I have tried to write a method that converts any IList of things into a comma-separated list of those things as a string. It looks like this:

public static string ToStringList<T>(this T source) where T : IList<object>
{
    string list_string = "[EMPTY]";
    try
    {
        if (source != null && source.Count() > 0)
        {
            list_string = "";
            foreach (var item in source)
            {
                //ToString unnecessarily written here to highlight the usage
                list_string += $", {item.ToString()}";
            }
        }
    }
    catch
    {
        list_string = "[ERROR - could not list values]";
    }
    list_string = list_string.StartsWith(", ") ? list_string.Substring(2) : list_string;
    return list_string;
}

I would like to use this method on an Observable collection of Sites:

public class Site
{
    public string Name { get; set; }
    public string code { get; set; }

    public override string ToString()
    {
        return Name;
    }
}

however, when I try to run the following code, I get a conversion error:

public ObservableCollection<Site> SelectedSites {get;set;}
//[some skipped code that inserts values into the ObservableCollection]

//Error: Cannot convert from Site to object
var sites = SelectedSites.ToStringList();

I understand why I get the conversion error - there is no way for the code to know how to convert the Site into an object. However, given that ToString() exists on everything, is there a way that I can alter the method ToStringList() such that it can accept an IList of any type?

I have read some articles and pages about IList (like this and this), but honestly they have as much confused as enlightened - is that because what I ask is impossible or so long winded as to be impractical (in which case I can find another way)?

I am using .NET Framework 4.8.

Upvotes: 0

Views: 464

Answers (2)

Sweeper
Sweeper

Reputation: 270820

Your extension method isn't available on an ObservableCollection<Site>, because an IList<Site> isn't related to IList<object> at all (See here for why).

You can instead use IList<T> as the parameter type:

public static string ToStringList<T>(this IList<T> source)

Now this will be available on ObservableCollection<Site>, because it implements IList<Site>, and the compiler can infer that T is Site.

Since you are not using any of the specific things that IList<T> provides, you can also define this method for the more general IEnumerable<T>. However, calling Count() on a general IEnumerable could be an O(n) operation. You might want to use Any() to check if there is any elements instead.

public static string ToStringList<T>(this IEnumerable<T> source)

Also note that you seem to be reinventing string.Join a little bit:

public static string ToStringList<T>(this IEnumerable<T> source)
{
    try
    {
        const string empty = "[EMPTY]";
        if (source != null)
        {
            return string.Join(", ", source.Select(x => x.ToString()).DefaultIfEmpty(empty));
        }
        else
        {
            return empty;
        }
    }
    catch
    {
        return "[ERROR - could not list values]";
    }
}

Upvotes: 2

MrMoeinM
MrMoeinM

Reputation: 2378

Change

public static string ToStringList<T>(this T source) where T : IList<object>

To

public static string ToStringList<T>(this IList<T> source) where T : class

Upvotes: 2

Related Questions