说我有这个对象
public class foo
{
Ibar data {get; set;}
}
public interface Ibar
{
int id { get; set; }
}
public class bar : Ibar
{
public int id {get; set;}
public string a {get; set;}
}
public class bar2 : Ibar
{
public int id {get; set;}
public DateTime b {get; set;}
}
现在我在这里实现像这样的方法
public something Method(foo f)
{
if(f.data is bar)
Console.WriteLine(((bar)f.data).doSomething());
else
Console.WriteLine(((bar2)f.data).doSomething());
}
public string doSomething(this bar b)
{
return b.a;
}
public string doSomething(this bar2 b)
{
return b.b.ToString();
}
到目前为止一直很好(see)。有没有办法让这部分更优雅?特别是如果我有Ibar
的多个实现,它会变得混乱......
if(f.data is bar)
Console.WriteLine(((bar)f.data).doSomething());
else
Console.WriteLine(((bar2)f.data).doSomething());
更新
所以doSomething
应该是一种扩展方法。这就是为什么它没有在界面中定义。换句话说:我不允许更改界面或其实现。
答案 0 :(得分:2)
您是否正在尝试专门的扩展课程?
假设您正在这样做。
然而,您可以使用dynamic
public static class BarExtensions
{
public static string Something(this Ibar b)
{
return doSomething((dynamic)b);
}
public static string doSomething( bar b)
{
return b.a;
}
public static string doSomething(bar2 b)
{
return b.b.ToString();
}
}
因此,您只需要使用正确类型调用Something
。
此类型可以轻松获取任何对象的.GetType()
(因此不会出现问题)
答案 1 :(得分:1)
我认为你可以在一个地方进行类型检查,因为这是多态性的一个很好的候选者。由于bar
和bar2
都实现了Ibar
,因此您可以将doSomething
的定义更改为以下内容。
public string doSomething(Ibar item)
{
return item is bar ? ((bar)item).a : ((bar2)item).b.ToString();
}
现在,当您想要显示它时,这就是您处理foo的方式:
public void Method(foo f)
{
Console.WriteLine(f.data);
}
您只需传递data
属性,因为doSomething
会在收到它时知道如何处理它。
<强>更新强> 根据您的评论,我正在扩展代码以使用我建议的模式匹配。请考虑以下
public string doSomething(Ibar item)
{
switch (item)
{
case bar b:
return b.a;
case bar2 b:
return b.b.ToString();
case barX b:
return b.X.ToString();
default:
return item.GetType().ToString(); // Just in case...
}
}
其中barX的定义是
public class barX : Ibar
{
public int id {get; set; }
public object X {get; set; }
}
因此,每当您添加Ibar的实现时,您都会在doSomething中提供它。所有客户端代码都将保持不变,因为他们所要做的就是将Ibar传递给doSomething
方法,该方法在上一个示例中使用模式匹配来确定项目的类型,并相应地起作用。
最终修改
看到你想要使用重载,你可以使用反射,但我不知道这是否符合你对优雅的定义。在这种情况下,它不会使用switch语句,并且只要它们处于写入的类型中就会找到你的方法。请考虑以下内容并更改我之前提供的DoSomething的定义。
public static string ProcessThings(Ibar item)
{
var theType = item.GetType();
Console.WriteLine(theType.Name);
MethodInfo method = typeof(Program).GetMethods()
.Where(x => x.IsStatic && x.Name == "DoSomething" && x.ToString().Contains(theType.Name))
.SingleOrDefault();
var ret = method.Invoke(null, new object[] { item });
return ret?.ToString();
}
public static string DoSomething(barX item)
{
return "foo";
}
public static string DoSomething(bar2 item)
{
return "bar";
}
这样,您调用ProcessThings
方法(为了简洁而重命名,但它仍然可以保留为DoSomething),它将确定要调用哪个重载。
答案 2 :(得分:0)
如果我记得很清楚你可以像generic type constraints这样玩:
public something Method<T>(T f) where T : IBar
{
//Stuff here, where assures you f is always IBar
}
这应该强制传入的参数属于您想要的类型。 这是我能想到的最优雅的方式。
其他是typeof和其他条件语句,无论如何都需要更多的方法内代码(if,else等)。
修改强> 由于OP似乎很难理解,因此我使用Microsoft Unit Testing结果编写了一个完全正常工作的代码示例。
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace UnitTests_ALL
{
[TestClass]
public class UnrelatedTests
{
[TestMethod]
public void InterfaceTest()
{
bar x = new bar { result = "Hi bar it's " };
bar2 xx = new bar2 { beforeResult = DateTime.Now };
Console.WriteLine(Method(x));
Console.WriteLine(Method(xx));
}
public string Method<T>(T f) where T : Ibar
{
return f.result;
}
}
public class foo
{
public foo(Ibar fooBar)
{
data = fooBar;
}
Ibar data { get; set; }
}
public interface Ibar
{
int id { get; set; }
string result { get; set; }
}
public class bar : Ibar
{
public int id { get; set; }
public string result { get; set; }
}
public class bar2 : Ibar
{
public int id { get; set; }
public DateTime beforeResult
{
set { result = value.ToString(); }
}
public string result { get; set; }
}
public static class Extensions
{
public static string doSomething(this bar b)
{
return b.result;
}
public static string doSomething(this bar2 b)
{
return b.result;
}
}
}
这是输出: