泛型显式转换

本文关键字:显式转换 泛型 | 更新日期: 2023-09-27 18:10:01

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

所以 => 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;

泛型显式转换

使用 Reflection 的示例:

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

也看看@Jon Skeet的这个答案 - 特别是关于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");

如果你通过(object),那么它只做一个类型检查转换或一个盒子/取消盒子(在IL术语中:一个unbox-any( - 它不会使用运算符。同时使用泛型和运算符的唯一方法是通过(dynamic)而不是(object),但这在运行时会做一些工作。

确实很丑陋,但测试通过了:

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

您可以通过在 abstract CanCastFromString类上定义泛型T,然后将Get()泛型函数约束到该抽象类,诱使编译器知道可以从string显式转换泛型。

我假设你在类中定义了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运算符也不会使用用户定义的转换运算符。

相关文章: