【问题标题】:Generics explicit conversion泛型显式转换
【发布时间】:2013-04-19 22:10:15
【问题描述】:

我实现了从字符串到名为 Foo 的对象的显式转换。

So => Foo f = (Foo)"foo data";有效

我需要实现一个将字符串转换为通用 T 的函数,在这种情况下,T 是 Foo 数据类型。

public T Get<T>(object o){
      // this always return false
      if (typeof(T).IsAssignableFrom(typeof(String)))
      {
            // when i by pass the if above this throws invalid cast exception
            return (T)(object)str;
      }
      return null; 
}

// When I call this, it generated an error
// Invalid cast from 'System.String' to Foo
Foo myObj = Get<Foo>("another foo object"); 

// when I use the dynamic keyword it works but this is C# 4.0+ feature, my function is in the older framework
return (T)(dynamic)str;

【问题讨论】:

  • 你应该改用TypeConverters。
  • Foo的定义是什么?是否有 implicit (msdn.microsoft.com/en-us/library/z5z9kes2%28v=vs.71%29.aspx) 或 explicit (msdn.microsoft.com/en-us/library/xhbhezf4%28v=vs.71%29.aspx) 运算符重载?
  • @CodingWithSpike Foo 有显式转换 Foo itm = (Foo)"string"
  • 看起来编译器不知道T 会有用户提供的explicit 运算符,所以简单地转换(T)str 不会编译。如果您将泛型限制为Foo,那么它可以构建并正常工作,但这违背了泛型的目的,因为您只能指定Foo。寻找一种方法来诱使编译器允许这样做...
  • @AaronLS 不会编译。

标签: c# generics casting


【解决方案1】:

一个使用反射的例子:

class Program
{
    static void Main(string[] args)
    {           
        Foo myObj = TypeResolver.Get<Foo>("Foo data");            
    }
}

class TypeResolver
{
    public static T Get<T>(object obj)
    {
        if (typeof(T).CanExplicitlyCastFrom<string>())
        {                             
            return obj.CastTo<T>();
        }
        return default(T);
    }
}

public static class Extensions
{
    public static bool CanExplicitlyCastFrom<T>(this Type type)
    {
        if (type == null)
            throw new ArgumentNullException("type");

        var paramType = typeof(T);
        var castOperator = type.GetMethod("op_Explicit", 
                                        new[] { paramType });
        if (castOperator == null)
            return false;

        var parametres = castOperator.GetParameters();
        var paramtype = parametres[0];
        if (paramtype.ParameterType == typeof(T))
            return true;
        else
            return false;
    }

    public static T CastTo<T>(this object obj)
    {            
        var castOperator = typeof(T).GetMethod("op_Explicit", 
                                        new[] { typeof(string) });
        if (castOperator == null)
            throw new InvalidCastException("Can't cast to " + typeof(T).Name);
        return (T)castOperator.Invoke(null, new[] { obj });
    }
}

【讨论】:

  • 这正是我需要的,谢谢!!! var castOperator = type.GetMethod("op_Explicit", new[] { paramType });
【解决方案2】:

还可以查看来自@Jon Skeet 的this answer - 特别是关于IsAssignableFrom 的引用。

我认为这不可能以您预想的方式实现。

我建议你在你的 Foo 类中加入一个“接口契约”——然后让泛型来完成它们的工作。

例如像这样的东西 - 但这只是我输入的一个快速解决方案......

class Factory 
{
    public static T Create<T, TVal>(TVal obj) where T : class, IFoo<TVal>, new()
    {
        return new T { Value = obj }; // return default(T);
    }
}
interface IFoo<TVal>
{
    TVal Value { get; set; }
}
class Foo : IFoo<string>
{
    public string Value { get; set; }
    public Foo() { }
}
// ...
public T Get<T, TVal>(TVal obj) where T : class, IFoo<TVal>, new()
{
    return Factory.Create<T, TVal>(obj);
}

您可以以类似的方式调用它 - 前提是您 have that luxury - 知道类型等。
(但你可以解决这个问题并根据需要进行调整)

Foo foo = Get<Foo, string>("another text");

【讨论】:

  • 所以 Type.IsAssignableFrom 是不可能的。除非我打算使用其他方法实现转换。
  • 恐怕是这样 - 在“泛型世界”中,显式/隐式似乎也不会以类似的方式发挥作用(我们需要 Eric 来解释这里的内部工作原理)。使用接口,你实际上可以强制你的泛型听你的。
【解决方案3】:

如果您通过(object) 进行操作,那么它只会进行类型检查强制转换或框/拆箱(在 IL 术语中:unbox-any) - 它不会使用运算符。将泛型和运算符一起使用的唯一方法是通过(dynamic) 而不是(object),但这在运行时会起到一点作用。

【讨论】:

  • 我在最后一行中提到,dynmic 是 c# 4.0 的特性,我被困在旧框架中。
  • @DuD。那么你不能通过泛型来做到这一点;这是唯一的办法。关于泛型的要点是相同的 IL 适用于所有类型。这对操作员不起作用。
【解决方案4】:

真的很丑,但是测试通过了:

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnitTestProject1
{
    [TestClass]
    public class UnitTest2
    {
        public T Get<T>(string str)
            where T : CanCastFromString<T>, ICanInitFromString, new()
        {
            return (T)str;
        }

        [TestMethod]
        public void Test()
        {
            var result = Get<Foo>("test");

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result, typeof(Foo));
            Assert.AreEqual("test", result.Value);
        }
    }

    public class Foo : CanCastFromString<Foo>
    {
        public string Value { get; set; }

        public override void InitFromString(string str)
        {
            Value = str;
        }
    }

    public abstract class CanCastFromString<T> : ICanInitFromString
        where T : CanCastFromString<T>, ICanInitFromString, new()
    {
        public static explicit operator CanCastFromString<T>(string str)
        {
            var x = new T();
            x.InitFromString(str);
            return x;
        }

        public abstract void InitFromString(string str);
    }

    public interface ICanInitFromString
    {
        void InitFromString(string str);
    }
}

你可以欺骗编译器知道你的泛型T 可以从string 显式转换,方法是在abstractCanCastFromString 上定义它,然后将Get() 泛型函数约束到该抽象类。

【讨论】:

    【解决方案5】:

    我假设您在课堂上定义了 T,但无论哪种方式,我都在方法上定义了它。如果您可以处理 T 上的类约束,则此方法有效。

    namespace TestCast {
        class Program
        {
            public static T Get<T>(string o) where T : class
            {
                return o as T;
            }
    
            static void Main(string[] args)
            {
                Get<Breaker>("blah");
            }
        }
    }
    

    如果转换无效,则返回 null,就像在 Du 的问题中,如果无法转换,则返回 null。对于字符串,这将在有限的情况下工作。 as 运算符也将使用用户定义的转换运算符。

    【讨论】:

    • 我的方法对 T 没有任何约束。为了使我的帖子更简单,我没有在方法中显示其他类型的转换。
    • 我并不是要在这里破坏你 :) 但我认为这行不通 - 实际上是 this returns null。但它确实可以编译。
    • 如果转换无效,则返回 null,就像在 Du 的问题中,如果无法转换,则返回 null。从最初的未经编辑的问题中不清楚周围的一些要求是什么。这确实有效,但在有限的情况下。
    • null 本身不是一个约束吗?
    • 我在说的是 - if Foo is class 和引用类型。并定义了显式或隐式运算符。这通过了编译(尽管无关紧要)-但as 不识别运算符并返回null。我试了一个例子。
    猜你喜欢
    • 1970-01-01
    • 2017-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-12
    • 1970-01-01
    • 2012-02-15
    • 1970-01-01
    相关资源
    最近更新 更多