Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/293.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 我可以选择字符串还是IEnumerable<;T>;,其中T是字符串或IEnumerable<;T>;?_C#_Generics - Fatal编程技术网

C# 我可以选择字符串还是IEnumerable<;T>;,其中T是字符串或IEnumerable<;T>;?

C# 我可以选择字符串还是IEnumerable<;T>;,其中T是字符串或IEnumerable<;T>;?,c#,generics,C#,Generics,我有一个处理字符串集合的实用方法,比如: public static string MyJoin(this IEnumerable<string> strings) { return string.Join(Environment.NewLine, strings); } 公共静态字符串MyJoin(此IEnumerable字符串) { 返回string.Join(Environment.NewLine,strings); } 我还希望能够

我有一个处理字符串集合的实用方法,比如:

    public static string MyJoin(this IEnumerable<string> strings)
    {
        return string.Join(Environment.NewLine, strings);
    }
公共静态字符串MyJoin(此IEnumerable字符串)
{
返回string.Join(Environment.NewLine,strings);
}
我还希望能够处理
IEnumerable
IEnumerable
等等,所有这些都是这样处理的:

    public static string MyJoin(this IEnumerable<IEnumerable<string>> strings)
    {
        return strings.Select(x => x.MyJoin()).MyJoin();
    }

    public static string MyJoin(this IEnumerable<IEnumerable<IEnumerable<string>>> strings)
    {
        return strings.Select(x => x.MyJoin()).MyJoin();
    }
公共静态字符串MyJoin(此IEnumerable字符串)
{
返回字符串。选择(x=>x.MyJoin()).MyJoin();
}
公共静态字符串MyJoin(此IEnumerable字符串)
{
返回字符串。选择(x=>x.MyJoin()).MyJoin();
}
有没有任何方法可以优雅地表达这一点,而不必硬编码我可能想要使用的所有可能的级别

  • 我曾经尝试过使用泛型,但我就是无法找出正确的约束(如果有)
  • 如果可能的话,我也希望避免声明新类型
  • 另一方面,性能并不重要

小心
IEnumerable
和字符串,因为字符串是
IEnumerable
,其中T是
char

使用多个重载的静态实现:

public static string MyJoin(this IEnumerable<IEnumerable<IEnumerable<string>>> items)
{
    return items.SelectMany(x => x).MyJoin();
}

public static string MyJoin(this IEnumerable<IEnumerable<string>> items)
{
    return items.SelectMany(x => x).MyJoin();
}

public static string MyJoin(this IEnumerable<string> strings)
{
    return string.Join(Environment.NewLine, strings);
}
公共静态字符串MyJoin(此IEnumerable items)
{
return items.SelectMany(x=>x).MyJoin();
}
公共静态字符串MyJoin(此IEnumerable items)
{
return items.SelectMany(x=>x).MyJoin();
}
公共静态字符串MyJoin(此IEnumerable字符串)
{
返回string.Join(Environment.NewLine,strings);
}
不能使用单个泛型方法并动态“展开”泛型参数

使用反射的实现:

public static string MyJoin<T>(this IEnumerable<T> items)
{
    if (typeof(T) == typeof(string)
    {
        return items.Cast<string>().MyStringJoin();
    }
    var innerIEnumerableType = typeof(T).GetInterfaces().FirstOrDefault(x => x.IsGeneric() 
        && x.GetGenericType() == typeof(IEnumerable<>);
    if (innerIEnumerableType != null)
    {
        // create generic method to 
        var method = typeof(ThisType).GetMethod("MyJoin", /* some flags */)
            .MakeGenericMethod(innerIEnumerableType.GetGenericArguments().First())

        // recursive call to generic method
        return items.Select(x => (string)method.Invoke(null, x)).MyStringJoin();
    }
    throw new InvalidOperationException("Type is not a (nested) enumarable of strings")
}

public static string MyStringJoin(this IEnumerable<string> strings)
{
    return string.Join(Environment.NewLine, strings);
}
公共静态字符串MyJoin(此IEnumerable items)
{
if(typeof(T)=typeof(string)
{
返回items.Cast().MyStringJoin();
}
var innerIEnumerableType=typeof(T).GetInterfaces().FirstOrDefault(x=>x.IsGeneric()
&&x.GetGenericType()==typeof(IEnumerable);
if(innerIEnumerableType!=null)
{
//创建泛型方法来
var method=typeof(ThisType).GetMethod(“MyJoin”,/*一些标志*/)
.MakeGenericMethod(innerIEnumerableType.GetGenericArguments().First())
//对泛型方法的递归调用
returnitems.Select(x=>(string)method.Invoke(null,x)).MyStringJoin();
}
抛出新的InvalidOperationException(“类型不是(嵌套的)可枚举字符串”)
}
公共静态字符串MyStringJoin(此IEnumerable字符串)
{
返回string.Join(Environment.NewLine,strings);
}

老实说,反射在这里没有用。如果它不是类型安全的,为什么要使用泛型呢?最后你也可以使用非泛型的
IEnumerable
。它更容易实现。见Denis Itskovich的解决方案,他设法写下了我实际尝试做的事情。

使用SelectMany扩展方法。

基于Stef安的解决方案,简化和编译固定版本

public static string MyJoin(this IEnumerable items)
{
    if (items is IEnumerable<string>)
    {
        return string.Join(Environment.NewLine, (IEnumerable<string>)items);
    }
    if (items is IEnumerable<IEnumerable>)
    {
        return items.Cast<IEnumerable>().Select(x => x.MyJoin())).MyJoin();
    }
    throw new InvalidOperationException("Type is not a (nested) enumarable of strings");
}
公共静态字符串MyJoin(此IEnumerable items)
{
如果(项是IEnumerable)
{
返回string.Join(Environment.NewLine,(IEnumerable)items);
}
如果(项是IEnumerable)
{
return items.Cast().Select(x=>x.MyJoin()).MyJoin();
}
抛出新的InvalidOperationException(“类型不是(嵌套的)可枚举字符串”);
}

部分受Stefan答案的启发,我找到了一个工作解决方案,该解决方案不使用泛型,而且(不幸的是)主要使用运行时类型检查进行编译:

    public static string MyJoin(this IEnumerable<string> strings)
    {
        return string.MyJoin(Environment.NewLine, strings);
    }
    public static string MyJoin(this IEnumerable<object> items)
    {
        if (items.All(x => x is string))
            return items.Cast<string>().MyJoin();

        if (items.All(x => x is IEnumerable<object>))
            return items.Cast<IEnumerable<object>>().Select(x => x.MyJoin()).MyJoin();

        throw new InvalidOperationException("The argument was not a nested enumerable of strings.");
    }
公共静态字符串MyJoin(此IEnumerable字符串)
{
返回string.MyJoin(Environment.NewLine,strings);
}
公共静态字符串MyJoin(此IEnumerable items)
{
if(items.All(x=>x是字符串))
返回items.Cast().MyJoin();
if(items.All(x=>x是IEnumerable))
return items.Cast().Select(x=>x.MyJoin()).MyJoin();
抛出新的InvalidOperationException(“该参数不是一个嵌套的可枚举字符串。”);
}

与其拥有一个未知类型的
IEnumerable
,不如在这里真正尝试创建一个基于树的结构。最有效的方法是创建一个可以表示其子节点的值的
节点

public class Node
{
    public Node(string value)
    {
        Value = value;
        Children = Enumerable.Empty<Node>();
    }
    public Node(string value, IEnumerable<Node> children)
    {
        Value = value;
        Children = children;
    }
    public string Value { get; private set; }
    public IEnumerable<Node> Children { get; private set; }
}

它不会编译,因为对于非泛型IEnumerable没有选择扩展方法。当然,仍然可以调用Enumerable。通过反射选择…True。我修复了它。您只能将项强制转换到对象以获得
IEnumerablte
,而不知道t。修复版本也不会编译,因为对于t没有强制转换方法(不受约束约束)。我认为唯一的方法是使用反射。我想你是想在调用select和object之前将其强制转换为
(IEnumerable)
(在第二个
中,如果
),否则它不会编译。但是,在一个简单的
IEnumerable[]
(字符串的枚举的枚举的枚举)上调用它错过这两个条件并命中异常。=/是的,你是对的。它不是这样工作的。递归调用必须使用正确的泛型参数t,因为t是针对其类型进行测试的。这需要反射。它可能太复杂了。使用单独的方法更容易,类型更安全。使用纯“泛型魔术”实现不可能。您可以切换到IEnumerable(不带t),以使其更易于实现,且类型安全性更低。(请参阅Denis Itskovich解决方案)很好的解决方案。我没有尝试完全删除泛型。但这正是我真正想要做的。我用一个纯类型安全的基于重载的解决方案和一个过于复杂的基于反射的解决方案替换了我的答案。。。
public static IEnumerable<T> Traverse<T>(T item, 
    Func<T, IEnumerable<T>> childSelector)
{
    var stack = new Stack<T>();
    stack.Push(item);
    while (stack.Any())
    {
        var next = stack.Pop();
        yield return next;
        foreach (var child in childSelector(next))
            stack.Push(child);
    }
}
var allValues = Traverse(rootNode, node => node.Children)
    .Select(node => node.Value);