繁体   English   中英

将通用输入对象传递给方法C#

[英]Passing Generic Input object to a method C#

我有一个方法ABC()从我的应用程序中的两个不同的地方调用。 从这两个地方我都有不同的类对象,它实现了一个共同的接口“IDestination”。

我的两个类和接口看起来像这样:

public class ClassA: IDestination
{
        public string Var1 { get; set; }    
        public string Var2 { get; set; }    
        public string Var3 { get; set; }
}

public class ClassB: IDestination
{
        public string Var1 { get; set; }
        public string Var2 { get; set; }
        public string Var3 { get; set; }
        public string Var4 { get; set; }
        public string Var5 { get; set; }
}

public interface IDestination
{
        string Var1 { get; set; }
        string Var2 { get; set; }
}

截至目前方法ABC()接受classA的对象,我希望它也可以接受classB的对象。 为此我已经使我的方法定义通用如下:

public string ABC<T>(T obj)
{

}

但是,问题在于我想要访问类的属性(classA和classB两者)的ABC方法。

public string ABC<T>(T obj)
 {
        //Some code
        obj.var2; //of classA

        obj.var4; //of classB
        //Some code
 }

我不能允许在界面上做任何更改。

我怎样才能做到这一点? 我不想创建另一种处理不同类对象的方法。 任何的想法?

首先, 接口不是继承的,而是实现的

另一方面,可以约束通用参数:

public string ABC<T>(T obj) where T : IDestination

OP说:

我不能允许在界面上做任何更改。

顺便说一句,根据您的要求和通用类型约束,您将无法同时接受classAclassB因为C#不支持多继承。 如果要访问classB成员,则需要扩展IDestination接口以定义要在类型为IDestinationclassB访问的所有属性:

public interface IDestination
{
      string Var1 { get; set; }
      string Var2 { get; set; }
      string Var3 { get; set; }
      string Var4 { get; set; }
      string Var5 { get; set; } 
}

或者,您可以使用其余属性定义第二个接口:

public interface IDestination2
{
      string Var3 { get; set; }
      string Var4 { get; set; }
      string Var5 { get; set; } 
}

...你将在classB上实现它:

public class ClassB: IDestination, IDestination2

无论如何,这里的问题是你不能约束泛型参数来接受某些给定类的两个不同的继承。 也就是说,例如,如果您将T约束为IDestinationIDestination2 ,则您将无法将ClassA作为参数,因为它不实现IDestination2

OP说:

我不想创建另一种处理不同类对象的方法。

实际上这也可以在没有泛型但是方法重载的情况下解决,即使你不想走这条路线也不是一个邪恶的方法:

public void ABC(IDestination destination) {}
public void ABC(IDestination2 destination) {}

// or directly...

public void ABC(ClassA destination) {}
public void ABC(ClassB destination) {}

否则,您需要按如下方式实现:

public string ABC<T>(T obj) where T : IDestination
{
     ClassA a = obj as ClassA;
     ClassB b = obj as ClassB;

     // Now if you want to access Var1, Var2 you can access them
     // using "obj" reference, because T is IDestination
     string var1 = obj.Var1;
     string var2 = obj.Var2;

     if(a != null) 
     {
         // Here access all ClassA members...
     }

     if(b != null)
     {
         // Here access all ClassB members...
     }
}

对我来说,上述方法是一个设计缺陷。 如果我使用泛型类型和接口是因为我想使用与泛型类型参数的类型相等的对象。

如果我开始对接口的特定实现执行向下转换 ,就像击败使用IDestination的目的一样,看起来你的方法可能只是接受object因为你的方法将访问ClassAClassB成员而不是IDestination

public string ABC(object obj)
{
     ClassA a = obj as ClassA;
     ClassB b = obj as ClassB;

     // Now if you want to access Var1, Var2 you can access them
     // using "obj" reference, because T is IDestination
     string var1 = obj.Var1;
     string var2 = obj.Var2;

     if(a != null) 
     {
         // Here access all ClassA members...
     }

     if(b != null)
     {
         // Here access all ClassB members...
     }

     return "";
}

TL; DR

总之,您应该使用接口来键入对象和通用约束,以保证在引用上最小化输入以避免强制转换。 否则,通用打字不是您的解决方案。

你应该明确地重新思考你的设计。 当您的方法接受接口的实例时,它应该适用于所有类型,而不仅仅是一个集合。 想象一下,您创建了另一个也实现接口的类型。 您必须重新实现整个方法来支持它。 因此,应该在接口而不是类级别定义所有属性,然后可以在方法中访问它们。

但是,如果您真的必须使用当前的方法,则只需将其强制转换为适当的类型:

ClassA a = obj as ClassA;
if (a != null) a.Var2 = ...
// this will fail if a user provides an instance of ClassC which also implements the interface
else ((ClassB)obj).Var4 = ...   

为此,您还需要对gegernic参数进行约束:

public string ABC<T>(T obj) where T : IDestination

我认为更好的设计如下:

public string ABC<T>(T obj) where T : IDestination
{

}

我这样说,因为你想要一个实现IDestination接口的对象的泛型方法,所以它作为一个设计会更好,如果你已经将它声明为约束。 为此,请看这里

然后,为了在ABC访问所需的字段,接口应具有所有这些属性。 因此,如果我们假设您希望类AB都具有相同的三个属性Var1Var2Var3并且您想要在ABC访问它们,那么您必须重新声明您的界面,如下所示:

public interface IDestination
{
    string Var1 { get; set; }
    string Var2 { get; set; }
    string Var3 { get; set; }
}

我认为在泛型方法中很难处理特定于类型的属性,因为它不知道这些属性的具体内容。 我建议将这个逻辑委托给一个方法,这个类将通过接口实现。

正如您在评论中提到的,您无法更改界面IDestination并且您不希望破坏兼容性,因为此解决方案已在许多地方使用。 所以这个命题不是改变已经工作的ClassAClassB逻辑,而是添加新的接口实现,它将覆盖你想在ABC方法中实现的这种类型条件逻辑。

我会添加另一个接口,它保存方法执行所有类型条件逻辑,并在ABC类的中间调用它。 为了保持您的泛型类型需要实现IDestination和IDestinationLogic接口的约束。 当然ABC方法可能需要稍作修改,但很难说它看起来有多精确,因为我们完全不了解它应该做什么。

示例实现可能如下所示:

public class GenericMethodClass
{

    public string ABC<T>(T obj) where T : IDestination, IDestinationLogic
    {
        var result = string.Empty;

        //some code happens here

        var typeConditionalLogicResult = obj.DoSomething(); 

        // do more stuff with according to the result of type-specific calculations

        return result;
    }
}


public class ClassA: IDestination, IDestinationLogic
{
    public string Var1 { get; set; }    
    public string Var2 { get; set; }    
    public string Var3 { get; set; }

    public string DoSomething()
    {
        return Var2;        
    }
}

public class ClassB: IDestination, IDestinationLogic
{
    public string Var1 { get; set; }
    public string Var2 { get; set; }
    public string Var3 { get; set; }
    public string Var4 { get; set; }
    public string Var5 { get; set; }

    public string DoSomething()
    {
        return Var4;        
    }
}

public interface IDestination
{
    string Var1 { get; set; }
    string Var2 { get; set; }   
}

public interface IDestinationLogic
{
    string DoSomething();   
}

我建议使用接口的方法与模板方法设计模式非常相似,模板方法设计模式最初包含抽象类。 您可以在这里阅读: http//www.dofactory.com/net/template-method-design-pattern也许它会为您的案例增添更多亮点

好吧,要实现你想要的,你需要实现ABC方法来接受一种IDestination:

public string ABC<T>(T obj) where T : IDestination

但要实现这一点,您必须更改界面以获得要访问的所有方法:

    public interface IDestination
    {
        string Var1 { get; set; }
        string Var2 { get; set; }
        string Var3 { get; set; }
        string Var4 { get; set; }
        string Var5 { get; set; }
    }

然后你必须改变你的类来实现接口的所有成员,这意味着你可能会得到这样的结果:

    public class ClassA : IDestination
    {
        public string Var1 { get; set; }
        public string Var2 { get; set; }
        public string Var3 { get; set; }

        public string Var4
        {
            get
            {
                throw new NotImplementedException();
            }

            set
            {
                throw new NotImplementedException();
            }
        }

        public string Var5
        {
            get
            {
                throw new NotImplementedException();
            }

            set
            {
                throw new NotImplementedException();
            }
        }
    }

    public class ClassB : IDestination
    {
        public string Var1 { get; set; }
        public string Var2 { get; set; }
        public string Var3 { get; set; }
        public string Var4 { get; set; }
        public string Var5 { get; set; }
    }

但是如果它们只抛出一个异常,那么在ClassA中实现额外的方法并没有多大意义。 所以你不妨完全实现它们。 但是ClassA和ClassB是完全相同的。 所以你必须问问自己,每个班级的独特之处是什么才能证明IDestination的单独具体实施? 回答这个问题将指导您做什么。

编辑:由于您无法更改界面,您是否被迫在方法ABC中使用界面? 你能创建一个ClassA和ClassB都继承的抽象类吗?

public abstract class ClassAB : IDestination //might not need the interface
{
  public virtual string Var1 { get; set; }
  public virtual string Var2 { get; set; }
  public virtual string Var3 { get; set; }
  public virtual string Var4 { get; set; }
  public virtual string Var5 { get; set; }
}

public class ClassA : ClassAB
{
   //override any of the virtual methods needed
}

public class ClassB : ClassAB
{
   //override any of the virtual methods needed
}

然后改变ABC方法:

public string ABC(ClassAB classAB)
{
  //all the methods are available on classAB
}

ClassA和ClassB仍然是IDestination类型,因为抽象父类实现它。 唯一的问题是,如果你可以改变方法ABC以期望一种类型的ClassAB而不是IDestination?

您可以使用反射类来处理该问题,您可以在运行时获取参数的属性,也可以使用。

Type type = typeof(obj);
PropertyInfo[] propertyList = type.GetProperties();

您可以使用Reflection获取这些值,如下所示:

public static string ABC<T>(T obj) 
    {
        string s=string.Empty;
        //Some code
        if(obj is ClassA)
            s = obj.GetType().GetProperty("Var2").GetValue(obj, null).ToString(); //of classA

        if (obj is ClassB)
        s = obj.GetType().GetProperty("Var4").GetValue(obj,null).ToString(); //of classB
        //Some code
        return s;
    }

暂无
暂无

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

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