代码之家  ›  专栏  ›  技术社区  ›  Jon

C#泛型和铸造问题

  •  3
  • Jon  · 技术社区  · 16 年前

    这是我目前的方法:

    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.
    

    我想做的是:

    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 回复  |  直到 16 年前
        1
  •  5
  •   Avish    16 年前

    没有办法将方法重载和泛型联系起来:尽管它们看起来相似,但它们却非常不同。具体地说,重载允许您这样做 不同的东西 基于使用的参数类型;而泛型允许您这样做 一模一样

    如果您的BuildClause方法被重载,并且每个重载都在做一些不同的事情(不仅仅是所使用的类型不同,在这种情况下,逻辑也确实不同——选择是否添加引号),那么最终,您将不得不说“如果类型是this do this,如果类型是this do this”(我称之为“接通型”)。

    另一种方法是避免“开关类型”逻辑,并用多态性替换它。假设你有一个 StringColFilter : ColFilter<string> IntColFilter : ColFilter<int> ,则它们中的每一个都可以从 ColFilter<T> 并提供自己的BuildClause实现(或者只是一些有助于BuildClause处理它的数据)。但是,您需要显式地创建ColFilter的正确子类型,它只是将“开关打开类型”逻辑移动到应用程序中的另一个位置。如果幸运的话,它会将该逻辑移动到应用程序中的某个位置,在该位置,您可以了解所处理的类型,然后您可以在应用程序中的不同位置显式创建不同的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
  •  2
  •   J.W.    16 年前

    函数BuildClause()看起来像什么。

    在我看来,您可以创建BuildClause()作为 扩大 方法,您可以将这些值附加在一起。我假设您只想在不同类型上调用.ToString()方法。

        3
  •  2
  •   jrista    16 年前

    如果在C#3.0中正确使用泛型,则可以通过隐式类型实现所需的功能(int 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)轻松确定类型。

        4
  •  2
  •   Amy B    16 年前

    铸造是一种运行时操作。

    此外,如果您使用了一个像样的sql参数化生成器,它将为您添加单引号。

        5
  •  1
  •   johnnycrash    16 年前

    我不明白这个问题。它对我有用。就这么简单吗?我错过了什么?

    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();
        }
    
      }
    
    }
    
        6
  •  0
  •   chris166    16 年前

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