亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

C#/F# 中基于約束的類型推斷

C#/F# 中基于約束的類型推斷

C#
慕虎7371278 2022-12-24 10:00:41
我一直在嘗試讓這個像靜態擴展這樣的東西工作一段時間:public static class MixedRepositoryExtensions {    public static Task<TEntity> FindBySelectorAsync<TRepository, TEntity, TSelector>(        this TRepository repository,        TSelector selector)        where TRepository : IReadableRepository<TEntity>, IListableRepository<TEntity>        where TEntity : class, ISearchableEntity<TSelector>        => repository.Entities.SingleOrDefaultAsync(x => x.Matches(selector));}然而,據我了解,C# 在設計上并未將通用約束作為其推理過程的一部分,導致在嘗試調用它時出現以下 CS0411 錯誤:無法從用法中推斷出方法“MixedRepositoryExtensions.FindBySelectorAsync(TRepository, TSelector)”的類型參數。嘗試明確指定類型參數。示例調用方法(其中 ProjectRepository 擴展了 IReadableRepository<Project> 和 IListableRepository<Project> 并且 project 擴展了 ISearchableEntity<int>):await (new ProjectRepository()).FindBySelectorAsync(0);我考慮過在所有調用者上顯式定義它們,但是,這種方法并不理想,因為它會在很多地方使用并且有很多長名稱類型。我還考慮過將兩個接口繼承為一個接口,如下所示:IReadableAndListableRepository<TEntity> :     IReadableRepository<TEntity>,    IListableRepository<TEntity>但是,由于我將不止使用一個擴展,而不僅僅是使用一個組合,我發現這會導致界面爆炸(如果是這樣的話?)。例如,這將是另一個:IUpdatableAndListableRepository<TEntity :    IUpdatableRepository<TEntity>,    IListableRepository<TEntity>我在這里從 Eric Lippert 那里找到了一個提示,即使用 F# 可能會有所幫助(因為我已經絕望了):泛型:為什么編譯器不能在這種情況下推斷類型參數?我玩了一下 F#,但發現很少有關于將類型約束到多個接口(或與此相關的任何特定接口)的文檔,并且無法克服一些錯誤。這是我最后一次嘗試。我意識到該方法不會返回相同的值,我只是暫時嘗試讓約束很好地發揮作用。抱歉,如果做得不好,這是我第一次玩 F#。[<Extension>]type MixedRepositoryExtensions() =    [<Extension>]    static member inline FindBySelectorAsync<'TSelector, 'TEntity when 'TEntity: not struct and 'TEntity:> ISearchableEntity<'TSelector>>(repository: 'TRepository when 'TRepository:> IReadableRepository<'TEntity> and 'TRepository:> IListableRepository<'TEntity>, selector: 'TSelector) = repository;但是,此實現會導致以下錯誤,均引用定義了 FindBySelectorAsync 的行:
查看完整描述

2 回答

?
守候你守候我

TA貢獻1802條經驗 獲得超10個贊

我懷疑問題的發生是因為TEntity只能間接定義,或者說是傳遞定義。對于編譯器,弄清楚是什么的唯一方法是深入TEntity檢查。TRepository但是,C# 編譯器不會深入檢查類型,而只會觀察它們的直接簽名。


我相信通過TRepository從等式中移除,你所有的麻煩都會消失:


public static class MixedRepositoryExtensions {

    public static Task<TEntity> FindBySelectorAsync<TEntity, TSelector>(

        this IReadableAndListableRepository<TEntity> repository,

        TSelector selector)

        where TEntity : class, ISearchableEntity<TSelector>

        => repository.Entities.SingleOrDefaultAsync(x => x.Matches(selector));

}

當您將此方法應用于實現存儲庫接口的具體對象時,它自己的通用類型參數將用于推斷FindBySelectorAsync方法的簽名。


如果問題在于能夠在幾個不相等的擴展方法中為存儲庫指定約束列表,那么我認為 .NET 平臺是限制,而不是 C# 本身。由于 F# 也編譯成字節代碼,因此 F# 中的泛型類型將受到與 C# 中相同的約束。


我找不到動態解決方案,即動態解決所有類型的解決方案。然而,有一種技巧可以保留完整的靜態類型功能,但需要每個具體存儲庫添加一個額外的屬性獲取器。此屬性不能作為擴展繼承或附加,因為它在每個具體類型中的返回類型會有所不同。這是演示這個想法的代碼(屬性簡稱為FixTypes):


public class EntityHolder<TTarget, TEntity>

{

    public TTarget Target { get; }


    public EntityHolder(TTarget target)

    {

        Target = target;

    }

}


public class PersonsRepository

    : IRepository<Person>, IReadableRepository<Person>,

      IListableRepository<Person>

{

    public IQueryable<Person> Entities { get; } = ...


    // This is the added property getter

    public EntityHolder<PersonsRepository, Person> FixTypes =>

        new EntityHolder<PersonsRepository, Person>(this);

}


public static class MixedRepositoryExtensions 

{

    // Note that method is attached to EntityHolder, not a repository

    public static Task<TEntity> FindBySelectorAsync<TRepository, TEntity, TSelector>(

        this EntityHolder<TRepository, TEntity> repository, TSelector selector)

        where TRepository : IReadableRepository<TEntity>, IListableRepository<TEntity>

        where TEntity : class, ISearchableEntity<TSelector>

        => repository.Target.Entities.SingleOrDefaultAsync(x => x.Matches(selector));

        // Note that Target must be added before accessing Entities

}

定義了屬性 getter 的存儲庫FixTypes可以以通常的方式使用,但擴展方法僅在其FixTypes屬性的結果上定義:


new PersonsRepository().FixTypes.FindBySelectorAsync(ageSelector);


查看完整回答
反對 回復 2022-12-24
?
慕森王

TA貢獻1777條經驗 獲得超3個贊

這個存儲庫結構是不是設計過度了?存儲庫要么是只讀的,要么是讀寫的。


public interface IReadOnlyRepository<TEntity>

    where TEntity : class

{

    Task<TEntity> FindAsync(TEntity entity);

    IQueryable<TEntity> Entities { get; }

    // etc.

}


// The read-write version inherits from the read-only interface.

public interface IRepository<TEntity> : IReadOnlyRepository<TEntity>

    where TEntity : class

{

    void Update(TEntity entity);

    void Insert(TEntity entity);

    // etc.

}

此外,您可以TSelector通過將設計更改為


public interface ISelector<TEntity>

    where TEntity : class

{

    bool Matches(TEntity entity);

}

現在,只需要一個類型參數


public static class MixedRepositoryExtensions {

    public static Task<TEntity> FindBySelectorAsync<TEntity>(

        this IReadOnlyRepository<TEntity> repository,

        ISelector<TEntity> selector

    ) where TEntity : class

        => repository.Entities.SingleOrDefaultAsync(x => selector.Matches(x));

}


查看完整回答
反對 回復 2022-12-24
  • 2 回答
  • 0 關注
  • 153 瀏覽

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號