C#泛型和类型转换问题

3

我遇到了一个问题,无法将对象强制转换为通用的IList。我有一组in语句来尝试解决这个问题,但肯定有更好的方法。

这是我的当前方法:

string values;

if (colFilter.Value is IList<int>)
{
    values = BuildClause((IList<int>)colFilter.Value, prefix);
}
else if (colFilter.Value is IList<string>)
{
    values = BuildClause((IList<string>)colFilter.Value, prefix);
}
else if (colFilter.Value is IList<DateTime>)
{
    values = BuildClause((IList<DateTime>)colFilter.Value, prefix);
}
else if (...) //etc.

What I want to do is this:

values = BuildClause((IList<colFilter.ColumnType>)colFilter.Value, prefix);

或者

values = BuildClause((IList<typeof(colFilter.ColumnType)>)colFilter.Value, prefix);

或者

values = BuildClause((IList<colFilter.ColumnType.GetType()>)colFilter.Value, prefix);

每个方法都会产生这个编译错误: 类型或命名空间名称“colFilter”无法找到(是否缺少 using 指令或程序集引用?)
在我的示例中,colFilter.ColumnType 是 int、string、datetime 等。我不确定为什么它不起作用。
有任何想法吗?
编辑:这是 C#2.0
编辑 #2
这是 BuildClause 方法(我为每种类型都有重载):
private static string BuildClause(IList<int> inClause, string strPrefix)
{
    return BuildClause(inClause, strPrefix, false);
}

private static string BuildClause(IList<String> inClause, string strPrefix)
{
    return BuildClause(inClause, strPrefix, true);
}

private static string BuildClause(IList<DateTime> inClause, string strPrefix)
{
    return BuildClause(inClause, strPrefix, true);
}
//.. etc for all types

private static string BuildClause<T>(IList<T> inClause, string strPrefix, bool addSingleQuotes)
    {
        StringBuilder sb = new StringBuilder();

        //Check to make sure inclause has objects
        if (inClause.Count > 0)
        {
            sb.Append(strPrefix);
            sb.Append(" IN(");

            for (int i = 0; i < inClause.Count; i++)
            {
                if (addSingleQuotes)
                {
                    sb.AppendFormat("'{0}'", inClause[i].ToString().Replace("'", "''"));
                }
                else
                {
                    sb.Append(inClause[i].ToString());
                }

                if (i != inClause.Count - 1)
                {
                    sb.Append(",");
                }
            }

            sb.Append(") ");
        }
        else
        {
            throw new Exception("Item count for In() Clause must be greater than 0.");
        }

        return sb.ToString();
    }
6个回答

5
没有办法将方法重载和泛型联系起来:尽管它们看起来相似,但它们非常不同。具体而言,重载允许您根据使用的参数类型执行不同的操作;而泛型允许您执行完全相同的操作,无论使用的类型如何。
如果您的BuildClause方法经过重载,并且每个重载都在做不同的事情(不仅仅是使用不同的类型,而是真正不同的逻辑,在这种情况下-选择是否添加引号),那么最终你必须说出类似于“如果类型是这样做这个,如果类型是那样做那个”(我称之为“switch-on-type”)。
另一种方法是避免“switch-on-type”逻辑,并用多态性替换它。假设您有一个StringColFilter:ColFilter<string>和一个IntColFilter:ColFilter<int>,那么每个过滤器都可以覆盖来自ColFilter<T>的虚拟方法并提供自己的BuildClause实现(或者只提供一些帮助BuildClause处理它的数据)。但是,然后您需要显式创建正确的ColFilter子类型,这只是将“switch-on-type”逻辑移动到应用程序中的另一个位置。如果您很幸运,它将把该逻辑移动到应用程序中您已经了解正在处理哪种类型的地方,然后您可以在应用程序的不同位置显式创建不同的ColFilter,并在以后进行通用处理。
考虑类似于这样的内容:
abstract class ColFilter<T> 
{
    abstract bool AddSingleQuotes { get; }
    List<T> Values { get; }
}

class IntColFilter<T>
{
    override bool AddSingleQuotes { get { return false; } }    
}

class StringColFilter<T>
{
    override bool AddSingleQuotes { get { return true; } }
}

class SomeOtherClass 
{
    public static string BuildClause<T>(string prefix, ColFilter<T> filter)
    {
        return BuildClause(prefix, filter.Values, filter.AddSingleQuotes);
    }

    public static string BuildClause<T>(string prefix, IList<T> values, bool addSingleQuotes) 
    {
        // use your existing implementation, since here we don't care about types anymore -- 
        // all we do is call ToString() on them.
        // in fact, we don't need this method to be generic at all!
    }
}

当然,这也带来了一个问题,即ColFilter是否应该知道引号的存在,但这是一个设计问题,需要另外提问。
我也赞同其他帖子的观点,如果你正在尝试通过连接字符串来创建SQL语句,那么你应该停止这样做,并转向使用参数化查询,这样更容易,更重要的是更安全。

2

如果你在C# 3.0中正确使用泛型,你可以通过隐式类型转换来实现所需功能 (在C# 2.0中你可能需要指定类型)。如果你的BuildClause方法是泛型的,它应该自动采用传递给它的泛型参数的任何类型:

public IList<T> BuildClause<T>(IList<T> value, object prefix)
{
    Type type = typeof(T);
    if (type == typeof(string))
    {
        // handle string
    }
    else if (type == typeof(int))
    {
        // handle int
    }
    // ...
}

public class ColumnFilter<T>:
    where T: struct
{
    public IList<T> Value { get; set; }
}

var colFilter = new ColumnFilter<string>
{
    Value = new { "string 1", "string 2", "string 3" }
}

IList<string> values = BuildClause(colFilter.Value, prefix);

使用泛型,您可以省略ColumnFilter的ColumnType属性。由于它是泛型的,与BuildClause方法一起,您可以轻松地通过typeof(T)确定类型。


2

函数BuildClause()长什么样子呢?

在我看来,你可以将BuildClause()作为IList的一个扩展方法创建,并将值附加在一起。我假设你只想在不同类型上调用.ToString()方法。


我运行了你的代码。我执行了 values = BuildClause(colFilter.Value, prefix); 看起来没问题。如果你确定 colFilter.Value 是 IList<T> 类型,那应该没问题。 - J.W.
那对我似乎不起作用,我得到了编译器错误:你有哪些BuildClause重载? - Jon

2

我在将对象转换为泛型时遇到了麻烦

转换是运行时操作。

泛型是编译时信息。

不要混淆两者。

另外,如果您使用的是一个好的 SQL 参数生成器,它会自动为您添加单引号。


1

我不明白这个问题。对我来说它是有效的。可能只需要删除强制转换,就可以解决问题了吗?我错过了什么吗?

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication1 {

  class Foo<T> : List<T> {
  }

  class Program {
    static void Main(string[] args) {

    var a = new Foo<int>();
    a.Add(1);
    var b = new Foo<string>();
    b.Add("foo");
    Console.WriteLine(BuildClause(a, "foo", true));
    Console.WriteLine(BuildClause(b, "foo", true));

    }

    private static string BuildClause<T>(IList<T> inClause, string strPrefix, bool addSingleQuotes) {
      StringBuilder sb = new StringBuilder();

      //Check to make sure inclause has objects
      if (inClause.Count == 0) 
        throw new Exception("Item count for In() Clause must be greater than 0.");
      sb.Append(strPrefix).Append(" IN(");
      foreach (var Clause in inClause) {
        if (addSingleQuotes) 
          sb.AppendFormat("'{0}'", Clause.ToString().Replace("'", "''"));
        else
          sb.Append(Clause.ToString());
        sb.Append(',');
      }
      sb.Length--;
      sb.Append(") ");
      return sb.ToString();
    }

  }

}

0

在编译时必须知道IList的类型。根据您想要执行的操作,您可能可以将列表强制转换为IList或IEnumerable(不使用泛型),然后对对象进行迭代。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接