【问题标题】:Restrict Type variable to specific class or subclass [closed]将类型变量限制为特定类或子类[关闭]
【发布时间】:2018-11-02 19:41:52
【问题描述】:

我正在使用 C# 并希望在我的类中存储一个类型,但我希望该类型属于某个类或其子类。

这可能是代码的样子:

public class Factory
{

    public Type typeToInstantiate where Type: ABaseType;

    public Factory(Type instantiateThis where Type : ABaseType)
    {
        this.typeToInstantiate = instantiateThis;
    }

    public void DoTheThing()
    {
        var newObj = (ABaseType)Activator.CreateInstance(typeToInstantiate);
        newObj.PrintSomethingToConsole();
    }
}


public abstract class ABaseType
{
    public abstract void PrintSomethingToConsole();
}

public abstract class ASubType : ABaseType
{
    public override void PrintSomethingToConsole()
    {
        System.Console.WriteLine("Something");
    }
}

public abstract class AnotherSubType : ABaseType
{
    public override void PrintSomethingToConsole()
    {
        System.Console.WriteLine("Something else");
    }
}

public static void Main(string[] args)
{
    List<Factory> factories = new List<Factory>();
    factories.Add(new Factory(typeof(AnotherSubType)));
    factories.Add(new Factory(typeof(ASubType)));

    foreach (var factory in factories) factory.PrintSomethingToConsole();
}

我要解决的问题(如果有人有更好的主意):我正在创建一种工厂,它将实例化一些指定类型的对象。所以,当这个工厂被创建时,它将被创建一个它应该实例化的类型。但是,这个工厂有一个特定的目的,所以它应该只实例化特定类(或该类的子类)的对象。

【问题讨论】:

  • 通常使用泛型类型约束来处理...
  • 不太清楚你在这里要求什么。似乎您正在描述抽象工厂设计模式,但您不希望工厂是抽象的。你觉得这合适吗?
  • 如果你想要一个可以处理任何类型的工厂,你想要class Factory&lt;T&gt;。如果您想要一个可以处理从给定基类派生的任何类型的类,您需要class Factory&lt;T&gt; where T: BaseClass,按照@Blindy 的解决方案。如果你想在运行时定义这个子类,你的验证只能在运行时进行。
  • 看来@Blindy 的解决方案正是你所需要的——你可以在类级别或方法级别使用泛型约束
  • @PaulRJones 你不是在之前的评论中写过这个吗:“当实例化这个工厂时,调用者应该指定它应该创建什么类型。”?调用者如何指定实例化什么类型,同时又不知道工厂应该实例化什么类型?

标签: c# types factory


【解决方案1】:

你是在问这样的事情吗:

class C<T> where T: SomeBaseClass
{
}

【讨论】:

  • 并非如此,尽管我考虑过类似的事情。如果我要使用这种方法,C 对象和 C 对象将是不同的类型,这是我不想要的。我希望“工厂”属于同一类型。
  • 但是stringint 是不同的类型......它们最近的共同祖先是object......
  • 我不确定你的意思是什么。
  • @PaulRJones 我不确定我是否关注,你想让一种工厂类型容纳多种对象吗?然后您是否在问如何将不相关的对象存储在一个列表中,同时只允许某个基类独立于工厂上的某些泛型类型?后者的答案是List&lt;object&gt;,并在你的Add函数中检查类型。
  • 我添加了一个简短的 sn-p,希望能更好地解释我在寻找什么。
【解决方案2】:

如果您需要在运行时进行检查,您可以在逻辑中使用Type'sIsAssignableFrom。例如

public class MyFactory
{
    Type baseType = typeof(MyBaseType);
    IDictionary<string, Type> typeMap = new Dictionary<string, Type>() {}
    public void RegisterType(string typeName, Type type) 
    {
        if (!(type == baseType || baseType.IsAssignableFrom(type)) throw new ArgumentException(nameof(type));
        typeMap.Add(typeName, type);    
    }
}

有关更详细的示例,请在此处查看我的代码:https://stackoverflow.com/a/53121973/361842

【讨论】:

  • 我也考虑过这个问题,但问题是错误是运行时而不是编译时。
  • 对于运行时,我认为您唯一的选择是根据 Amy 的评论/Blindy 的回答使用泛型。 stackoverflow.com/a/53124955/361842
【解决方案3】:

给定以下类:

public abstract class ABaseType
{
    public abstract void PrintSomethingToConsole();
}

public class ASubType : ABaseType
{
    public override void PrintSomethingToConsole()
    {
        System.Console.WriteLine("Something");
    }
}

public class AnotherSubType : ABaseType
{
    public override void PrintSomethingToConsole()
    {
        System.Console.WriteLine("Something else");
    }
}

(快速说明:AnotherSubType 和 ASubType 类不能是抽象的,如果你想创建它们的实例)。

你的工厂可以这样重构:

public class Factory 
{
    private readonly List<ABaseType> _instances;

    public Factory()
    {
        _instances = new List<ABaseType>();
    }

    public void Register<T>()  where T : ABaseType, new()
    {
        // creates a new instance of the object
        // As an alternative, save the type and create a new instance in DoTheThing()
        var instance = new T();
        _instances.Add(instance);
    }

    public void DoTheThing()
    {
        _instances.ForEach(x => x.PrintSomethingToConsole());
    }
}

使用代码:

var factory = new Factory();
factory.Register<AnotherSubType>();
factory.Register<ASubType>();

factory.DoTheThing();

输出:

其他的

某事

【讨论】:

  • 第一个解决方案的问题是它不能存在于具有生成不同类型的工厂的集合中。第二种解决方案的问题是它要求 CreateFruit 的调用者知道它应该实例化的类型。
  • @PaulRJones 恐怕我完全不知道您需要什么。也许你应该添加一些代码来展示如何使用工厂。
  • @PaulRJones 这似乎越来越像XYPropblem。我认为是时候坐下来想想你想要解决的问题(使用工厂),而不是如何实现工厂本身。也许整个方法是错误的,工厂根本不是你需要的——或者问题可能不在于工厂类,而在于软件的其他部分。
  • @ZoharPeled 说得好
  • 这就是为什么我描述了我试图解决的问题以及我试图解决它的方式。
【解决方案4】:

我理解您的要求如下:

  1. 您需要一个可以包含其他类型的类,仅限于 BaseType 的子类,即类型安全。
  2. 此类的不同实例必须能够存储在同一类型的变量中。

您可以使用协方差和类型约束来做到这一点。

声明协变接口:

public interface IMyFactory<out T>
{
    T Resolve();
}

在你的课堂上实现它:

public class MyFactory<T> : IMyFactory<T> where T :BaseType, new()
{
    public readonly Type typeToInstantiate;

    public MyFactory()
    {
        typeToInstantiate = typeof(T);
    }

    public T Resolve()
    {
        return new T();
    }
}

然后称呼它:

public class Program
{
    public static void Main()
    {
        IMyFactory<BaseType> factory;
        {
            factory = new MyFactory<BaseType>();
            var instance = factory.Resolve();
            Console.WriteLine(instance.GetType().FullName);
        }
        {
            factory = new MyFactory<DerivedType>() as IMyFactory<BaseType>;
            var instance = factory.Resolve();
            Console.WriteLine(instance.GetType().FullName);
        }

    }
}

注意这里只声明了一个变量,但它可以包含任一类型的工厂,并且每个工厂将在运行时解析为正确的类型,如输出所示。

输出:

Example.BaseType
Example.DerivedType

这是上面的link to a DotNetFiddle

【讨论】:

  • 这将存储一个对象。我想存储一个类型。
  • 请看我修改后的答案。
猜你喜欢
  • 1970-01-01
  • 2012-12-21
  • 1970-01-01
  • 2017-08-10
  • 1970-01-01
  • 2021-08-10
  • 1970-01-01
  • 2012-03-21
  • 2018-05-15
相关资源
最近更新 更多