简体   繁体   English

C#泛型和铸造问题

[英]C# Generics and Casting Issue

I am having trouble casting an object to a generic IList. 我无法将对象转换为通用IList。 I have a group of in statements to try to work around this, but there has to be a better way to do this. 我有一组in语句试图解决这个问题,但必须有一个更好的方法来做到这一点。

This is my current method: 这是我目前的方法:

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);

or 要么

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

or 要么

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

Each of these produces this compiler error: The type or namespace name 'colFilter' could not be found (are you missing a using directive or an assembly reference?) 其中每个都会产生此编译器错误:找不到类型或命名空间名称'colFilter'(您是否缺少using指令或程序集引用?)

In my example, colFilter.ColumnType is int, string, datetime, etc. I am not sure why this does not work. 在我的例子中,colFilter.ColumnType是int,string,datetime等。我不知道为什么这不起作用。

Any ideas? 有任何想法吗?

EDIT: This is C#2.0 编辑:这是C#2.0

EDIT #2 编辑#2

Here is the BuildClause method (I have overloads for each type): 这是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();
    }

There's no way to relate method overloading and generics: although they look similar, they are very different. 没有办法将方法重载和泛型联系起来:尽管它们看起来很相似,但它们却截然不同。 Specifically, overloading lets you do different things based on the type of arguments used; 具体来说,重载允许您根据使用的参数类型执行不同的操作 ; while generics allows you to do the exact same thing regardless of the type used. 而泛型允许你做同样的事情,无论使用何种类型。

If your BuildClause method is overloaded and every overload is doing something different (not just different by the type used, but really different logic, in this case - choosing whether or not to add quotes) then somewhere, ultimately, you're gonna have to say something like "if type is this do this, if type is that do that" (I call that "switch-on-type"). 如果您的BuildClause方法被重载并且每个重载都做了不同的事情(不仅仅是使用的类型不同,但是逻辑上真的不同,在这种情况下 - 选择是否添加引号)然后在某个地方,最终,你将不得不说“如果类型是这样做,如果类型就是这样”(我称之为“开启类型”)。

Another approach is to avoid that "switch-on-type" logic and replace it with polymorphism. 另一种方法是避免使用“开启类型”逻辑并将其替换为多态。 Suppose you had a StringColFilter : ColFilter<string> and a IntColFilter : ColFilter<int> , then each of them could override a virtual method from ColFilter<T> and provide its own BuildClause implementation (or just some piece of data that would help BuildClause process it). 假设你有一个StringColFilter : ColFilter<string>和一个IntColFilter : ColFilter<int> ,那么每个人都可以从ColFilter<T>覆盖一个虚方法,并提供自己的BuildClause实现(或者只是一些有助于BuildClause的数据)处理它)。 But then you'd need to explicitly create the correct subtype of ColFilter, which just moves the "switch-on-type" logic to another place in your application. 但是,您需要显式创建正确的ColFilter子类型,它只是将“开关类型”逻辑移动到应用程序中的另一个位置。 If you're lucky, it'll move that logic to a place in your application where you have the knowledge of which type you're dealing with, and then you could explicitly create different ColFilters at different places in your application and process them generically later on. 如果您很幸运,它会将该逻辑移动到您应用程序中您知道要处理的类型的位置,然后您可以在应用程序的不同位置明确创建不同的ColFilter并一般地处理它们稍后的。

Consider something like this: 考虑这样的事情:

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!
    }
}

Of course this also gets you to the problem of whether ColFilter should know about quotes or not, but that's a design issue and deserves another question :) 当然这也让你了解ColFilter是否应该知道引用的问题,但这是一个设计问题,值得另一个问题:)

I also stand by the other posters in saying that if you're trying to build something that creates SQL statements by joining strings together, you should probably stop doing it and move over to parameterized queries which are easier and, more importantly, safer. 我还支持其他海报说,如果你试图通过将字符串连接在一起来构建创建SQL语句的东西,你应该停止这样做并转移到更容易,更重要的是更安全的参数化查询。

What does the function BuildClause() look like. BuildClause()函数是什么样的。

It seems to me that you can create BuildClause() as an extension method on IList, and you can append the values together. 在我看来,您可以在IList上创建BuildClause()作为扩展方法,并且可以将值附加在一起。 I assume that you just want to call .ToString() method on different types. 我假设您只想在不同类型上调用.ToString()方法。

If you use generics properly in C# 3.0, you can achieve what you need through implicit typing (int C# 2.0 you might need to specify the type). 如果在C#3.0中正确使用泛型,则可以通过隐式类型实现所需(int C#2.0,您可能需要指定类型)。 If your BuildClause method is made generic, it should automatically take on whatever type is passed in to its generic parameter(s): 如果您的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);

With generics, you can drop the ColumnType property of your ColumnFilter. 使用泛型,您可以删除ColumnFilter的ColumnType属性。 Since it is generic, along with your BuildClause method, you are easily able to determine the type by doing typeof(T). 由于它是通用的,与您的BuildClause方法一起,您可以通过执行typeof(T)轻松确定类型。

I am having trouble casting an object to a generic 我无法将对象转换为泛型

Casting is a run-time operation. Casting是一个运行时操作。

Generic is compile-time information. Generic是编译时信息。

Don't cross the streams. 不要越过溪流。

Also - if you used a decent sql parameterization generator - it will add the single quotes for you. 另外 - 如果你使用了一个像样的sql参数化生成器 - 它将为你添加单引号。

I don't understand the question. 我不明白这个问题。 It works for me. 这个对我有用。 It could be as simple as droping the cast? 它可以像放弃演员一样简单吗? What am I missing? 我错过了什么?

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();
    }

  }

}

The type for the IList must be known at compile time. 必须在编译时知道IList的类型。 Depending on what you want to do, you might be able to cast the list to an IList or IEnumerable (without generics) and then iterate over the objects 根据您的目的,您可以将列表转换为IList或IEnumerable(无泛型),然后迭代对象

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM