是否可以“扩展”属性“类”?

4

我刚接触C#(上周开始),所以请对我友善一些 ;). 我想知道是否可以编写自定义属性,让我解释一下:

我有一些部分类,通过添加属性来完成,但所有getter和setter的模式都相同,因此我希望将其因式分解:

public partial class Travel
{
    public String TravelName
    {
        get
        {
            return LocaleHelper.GetRessource(Ressource1);
        }
        set
        {
            if (this.Ressource1 == null)
                Ressource1 = new Ressource() { DefaultValue = value };
            else
                Ressource1.DefaultValue = value;
        }
    }

    public String TravelDescription
    {
        get
        {
            return LocaleHelper.GetRessource(Ressource2);
        }
        set
        {
            if (this.Ressource2 == null)
                Ressource2 = new Ressource() { DefaultValue = value };
            else
                Ressource2.DefaultValue = value;
        }
    }
}

正如您所看到的,唯一变化的是Ressource1/Ressource2。 我的目标是能够编写类似以下内容的东西:

public partial class Travel
{
    public LocalizedString TravelName(Ressource1);

    public LocalizedString TravelDescription(Ressource2);
}

有没有想法可以使我的代码更简洁?或者有其他想法可以实现这个功能吗? 谢谢, Guillaume


你是否了解System.Globalization命名空间/概念? - Chris Richner
是的,但我在网上找到的关于使用数据库存储资源数据的信息非常“混乱”(令人头痛)... - Guillaume86
7个回答

7

在C#或.NET本身中没有做这件事的设施,但如果你需要大量处理这种情况,可以考虑通过postsharp进行面向方面编程。基本上,它将允许您定义一个属性,在编译时注入额外的代码。您输入的代码可能是以下内容:

public partial class Travel
{
    [LocalizedProperty(source = "Ressource1")
    public string TravelName { get; set; }

    [LocalizedProperty(source = "Ressource2")
    public string TravelDescription{ get; set; }
}

在编译时,PostSharp将使用您在新的LocalizedPropertyAttribute类中定义的模板替换属性。

3

您描述的那样并不能让它完全简洁,但是您可以减少setter方法的复杂度和冗余。

private void SetRessource(ref Ressource res, string value)
{
    if(res == null) res = new Ressource();

    res.DefaultValue = value;
}

public String TravelName
{
    get { return LocaleHelper.GetRessource(Ressource1); }
    set { SetRessource(ref this.Ressource1, value); }
}

public String TravelDescription
{
    get { return LocaleHelper.GetRessource(Ressource2); }
    set { SetRessource(ref this.Ressource2, value); }
}

好主意。另一个建议是将get、set和Ressource成员作为接口的一部分,比如说INameProvider。 - mjv

1
我不确定你想要达到什么目的,但你可能把事情搞得太复杂了。这样做不已经足够了吗?
public class Travel
{
   /// <summary>
   /// Creates a new instance of <see cref="Travel"/>.
   /// </summary>
   public Travel()
   {
      this.TravelName = Resources.DefaultTravelName;
      this.TravelDescription = Resources.DefaultTravelDescription;
   }

   public string TravelName { get; set; }

   public string TravelDescription { get; set; }
}

其中Resources是一个生成的类(来自resx文件),用于本地化资源。我有一种感觉,你正在尝试构建自己的本地化框架,因为你还不知道.NET 已经有了相关基础设施


实际上,Ressources是带有“en”,“fr”,“es”等字符串字段的Linq to SQL实体。我的LocaleHelper.GetRessource(Ressource)返回正确的语言。 (我需要将资源存储在数据库中,并尝试使用默认的全球化系统,但使用数据库代替resx非常困难...)。 我的自定义属性可以让我创建漂亮的LINQ请求和数据绑定。 - Guillaume86
顺便说一句,你的解决方案对于获取部分是完美的,但不幸的是设置部分要复杂一些... - Guillaume86

0

不,没有这样的方法。在 PHP 中可能会有可能, 但在 C# 中不行。

在这种情况下,您应该改变自己的方法。

更新: 也许你可以为每个属性使用类似于这样的东西(除了它明显的弱点):

public class Prop
{
    Resource _res;

    public Prop(Resource res)
    {
        this._res = res;
    }

    public string Value
    {
        get
        {
            return LocaleHelper.GetRessource(_res);
        }
        set
        {
            if(_res == null)
                // This is a weak point as it's now
                // as it wont work
            else
                _res.DefaultValue = value;
        }
}

0
你可以实现一个单一的索引属性,根据你的喜好,提供以下两种语法选择之一。代码基本上是一个函数,接受特定命名的资源并返回正确的内容。
Travel t = new Travel();
string x = t["Name"];
    or 
string x = t[Travel.Name];

0
你可以通过将getter和setter逻辑封装在一个基类中,然后从任何新属性中简单地调用这些方法(只需作为这些方法的薄包装器),来使你的生活更轻松。以下是一个示例:
public class Travel : LocalizedRessourceSubscriber
{

    private Ressource<string> Ressource1 = null;
    private Ressource<string> Ressource2 = null;

    public String TravelName { 
        get { return GetRessource<string>(Ressource2); }
        set { SetRessource<string>(Ressource1, value); } 
    }

    public String TravelDescription {
        get { return GetRessource<string>(Ressource2); }
        set { SetRessource<string>(Ressource2, value); } 
    }

}

public class LocalizedRessourceSubscriber
{

    protected T GetRessource<T>(Ressource<T> Source)
    {
        return LocaleHelper.GetRessource<T>(Source);
    }

    protected void SetRessource<T>(Ressource<T> Source, T Value)
    {
       (Source ?? 
           (Source = new Ressource<T>())
                ).DefaultValue = Value;
    }

}

这样,你的属性中就几乎没有逻辑,而且重复的代码也更少。这假设以下类(我将它们泛型化):

public static class LocaleHelper
{
    public static T GetRessource<T>(Ressource<T> Source)
    {
        return default(T);
    }
}

public class Ressource<T>
{
    public T DefaultValue { get; set; }
}

-1

这没有意义。按照你目前的属性使用方式,你可以简单地写成:

   Travel t = new Travel();
   string tvlName = t.TravelName;    
   string desc = t.TravelDescription;

如果你更改了想要的方式,你也必须指定参数

   Travel t = new Travel();
   LocalizedString tvlName = t.TravelName([someresopurcedesignator]);    
   LocalizedString desc = t.TravelDescription([someresopurcedesignator]);  

你所能做的就是制作一个“propertyBag”仿真器

   public class Travel 
   {
       private LocalizedString props = new LocalizedString();
       public LocalizedString Propertys
       {
          get { return props; }
          set { props = value; }
       }

   }

   public class LocalizedString // this is property Bag emulator
   {
       public string this[string resourceName]
       {
           get{ return LocaleHelper.GetRessource(resourceName); }
           set{ LocaleHelper.GetRessource(resourceName) = value; }
       }
   }

你可以这样访问:

   Travel t = new Travel();
   t.Propertys[NameResource1] = "Bob Smith";
   t.Propertys[DescriptionResource2] = "Fun trip to discover the orient";
   string tvlName = t.Propertys[NameResource1];    
   string desc    = t.Propertys[DescriptionResource2];    

我认为您误解了OP的意图。他的语法并未暗示需要对属性调用进行参数化;他仅希望将实现参数化。 - Jeff Yates
@Jeff,也许我还是没有理解...如果实现是参数化的,如果调用没有参数化,那么参数值如何传递到实现中呢? - Charles Bretana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接